Ombi will now tell you when there is an update available in the settings, clicking that will also provide you with the changelog and links to manually download the binaries.

Also removed references to the custom HTTP client implimentation and we now use the inbuilt IHttpClientFactory, this means we have removed the "Ignore Certificate Errors" option in Ombi as it's no longer needed.
This commit is contained in:
tidusjar 2021-04-04 23:35:38 +01:00
parent b59a792fdf
commit 81f410d782
27 changed files with 220 additions and 303 deletions

View file

@ -16,14 +16,14 @@ namespace Ombi.Api
{ {
public class Api : IApi public class Api : IApi
{ {
public Api(ILogger<Api> log, IOmbiHttpClient client) public Api(ILogger<Api> log, HttpClient client)
{ {
Logger = log; Logger = log;
_client = client; _client = client;
} }
private ILogger<Api> Logger { get; } private ILogger<Api> Logger { get; }
private readonly IOmbiHttpClient _client; private readonly HttpClient _client;
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{ {

View file

@ -1,14 +0,0 @@
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace Ombi.Api
{
public interface IOmbiHttpClient
{
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request);
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
Task<string> GetStringAsync(Uri requestUri);
}
}

View file

@ -10,6 +10,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Polly" Version="7.1.0" /> <PackageReference Include="Polly" Version="7.1.0" />

View file

@ -1,110 +0,0 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2017 Jamie Rees
// File: OmbiHttpClient.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 System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Ombi.Core.Settings;
using Ombi.Helpers;
using Ombi.Settings.Settings.Models;
namespace Ombi.Api
{
/// <summary>
/// The purpose of this class is simple, keep one instance of the HttpClient in play.
/// There are many articles related to when using multiple HttpClient's keeping the socket in a WAIT state
/// https://blogs.msdn.microsoft.com/alazarev/2017/12/29/disposable-finalizers-and-httpclient/
/// https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
/// </summary>
public class OmbiHttpClient : IOmbiHttpClient
{
public OmbiHttpClient(ICacheService cache, ISettingsService<OmbiSettings> s)
{
_cache = cache;
_settings = s;
_runtimeVersion = AssemblyHelper.GetRuntimeVersion();
}
private static HttpClient _client;
private static HttpMessageHandler _handler;
private readonly ICacheService _cache;
private readonly ISettingsService<OmbiSettings> _settings;
private readonly string _runtimeVersion;
public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
{
await Setup();
return await _client.SendAsync(request);
}
public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
await Setup();
return await _client.SendAsync(request, cancellationToken);
}
public async Task<string> GetStringAsync(Uri requestUri)
{
await Setup();
return await _client.GetStringAsync(requestUri);
}
private async Task Setup()
{
if (_client == null)
{
if (_handler == null)
{
// Get the handler
_handler = await GetHandler();
}
_client = new HttpClient(_handler);
_client.DefaultRequestHeaders.Add("User-Agent", $"Ombi/{_runtimeVersion} (https://ombi.io/)");
}
}
private async Task<HttpMessageHandler> GetHandler()
{
if (_cache == null)
{
return new HttpClientHandler();
}
var settings = await _cache.GetOrAdd(CacheKeys.OmbiSettings, async () => await _settings.GetSettingsAsync(), DateTime.Now.AddHours(1));
if (settings.IgnoreCertificateErrors)
{
return new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true,
};
}
return new HttpClientHandler();
}
}
}

View file

@ -23,7 +23,6 @@ using Ombi.Notifications;
using Ombi.Schedule; using Ombi.Schedule;
using Ombi.Schedule.Jobs; using Ombi.Schedule.Jobs;
using Ombi.Settings.Settings; using Ombi.Settings.Settings;
using Ombi.Store.Context;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using Ombi.Notifications.Agents; using Ombi.Notifications.Agents;
using Ombi.Schedule.Jobs.Radarr; using Ombi.Schedule.Jobs.Radarr;
@ -68,6 +67,8 @@ using Ombi.Api.MusicBrainz;
using Ombi.Api.Twilio; using Ombi.Api.Twilio;
using Ombi.Api.CloudService; using Ombi.Api.CloudService;
using Ombi.Api.RottenTomatoes; using Ombi.Api.RottenTomatoes;
using System.Net.Http;
using Microsoft.Extensions.Logging;
namespace Ombi.DependencyInjection namespace Ombi.DependencyInjection
{ {
@ -119,14 +120,24 @@ namespace Ombi.DependencyInjection
public static void RegisterHttp(this IServiceCollection services) public static void RegisterHttp(this IServiceCollection services)
{ {
var runtimeVersion = AssemblyHelper.GetRuntimeVersion();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<IPrincipal>(sp => sp.GetService<IHttpContextAccessor>().HttpContext.User); services.AddScoped<IPrincipal>(sp => sp.GetService<IHttpContextAccessor>().HttpContext.User);
services.AddHttpClient("OmbiClient", client =>
{
client.DefaultRequestHeaders.Add("User-Agent", $"Ombi/{runtimeVersion} (https://ombi.io/)");
}).ConfigurePrimaryHttpMessageHandler(() =>
{
var httpClientHandler = new HttpClientHandler();
httpClientHandler.ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true;
return httpClientHandler;
});
} }
public static void RegisterApi(this IServiceCollection services) public static void RegisterApi(this IServiceCollection services)
{ {
services.AddScoped<IApi, Api.Api>(); services.AddScoped<IApi, Api.Api>(s => new Api.Api(s.GetRequiredService<ILogger<Api.Api>>(), s.GetRequiredService<IHttpClientFactory>().CreateClient("OmbiClient")));
services.AddScoped<IOmbiHttpClient, OmbiHttpClient>(); // https://blogs.msdn.microsoft.com/alazarev/2017/12/29/disposable-finalizers-and-httpclient/
services.AddTransient<IMovieDbApi, Api.TheMovieDb.TheMovieDbApi>(); services.AddTransient<IMovieDbApi, Api.TheMovieDb.TheMovieDbApi>();
services.AddTransient<IPlexApi, PlexApi>(); services.AddTransient<IPlexApi, PlexApi>();
services.AddTransient<IEmbyApi, EmbyApi>(); services.AddTransient<IEmbyApi, EmbyApi>();

View file

@ -13,6 +13,7 @@
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="5.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" /> <PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -5,6 +5,6 @@ namespace Ombi.Schedule.Jobs.Ombi
public interface IOmbiAutomaticUpdater : IBaseJob public interface IOmbiAutomaticUpdater : IBaseJob
{ {
string[] GetVersion(); string[] GetVersion();
Task<bool> UpdateAvailable(string branch, string currentVersion); Task<bool> UpdateAvailable(string currentVersion);
} }
} }

View file

@ -49,10 +49,10 @@ namespace Ombi.Schedule.Jobs.Ombi
var productArray = productVersion.Split('-'); var productArray = productVersion.Split('-');
return productArray; return productArray;
} }
public async Task<bool> UpdateAvailable(string branch, string currentVersion) public async Task<bool> UpdateAvailable(string currentVersion)
{ {
var updates = await Processor.Process(branch); var updates = await Processor.Process();
var serverVersion = updates.UpdateVersionString; var serverVersion = updates.UpdateVersionString;
return !serverVersion.Equals(currentVersion, StringComparison.CurrentCultureIgnoreCase); return !serverVersion.Equals(currentVersion, StringComparison.CurrentCultureIgnoreCase);
@ -88,7 +88,7 @@ namespace Ombi.Schedule.Jobs.Ombi
Logger.LogDebug(LoggingEvents.Updater, "Looking for updates now"); Logger.LogDebug(LoggingEvents.Updater, "Looking for updates now");
//TODO this fails because the branch = featureupdater when it should be feature/updater //TODO this fails because the branch = featureupdater when it should be feature/updater
var updates = await Processor.Process(branch); var updates = await Processor.Process();
Logger.LogDebug(LoggingEvents.Updater, "Updates: {0}", updates); Logger.LogDebug(LoggingEvents.Updater, "Updates: {0}", updates);

View file

@ -109,8 +109,8 @@ namespace Ombi.Core.Processor
public string UpdateVersionString { get; set; } public string UpdateVersionString { get; set; }
public int UpdateVersion { get; set; } public int UpdateVersion { get; set; }
public DateTime UpdateDate { get; set; } public DateTime UpdateDate { get; set; }
public bool UpdateAvailable { get; set; }
public List<ChangeLog> ChangeLogs { get; set; } public string ChangeLogs { get; set; }
public List<Downloads> Downloads { get; set; } public List<Downloads> Downloads { get; set; }
} }

View file

@ -16,94 +16,41 @@ namespace Ombi.Schedule.Processor
{ {
public class ChangeLogProcessor : IChangeLogProcessor public class ChangeLogProcessor : IChangeLogProcessor
{ {
public ChangeLogProcessor(IApi api, IOmbiHttpClient client) public ChangeLogProcessor(IApi api, IHttpClientFactory client)
{ {
_api = api; _api = api;
_client = client; _client = client.CreateClient("OmbiClient");
} }
private readonly IApi _api; private readonly IApi _api;
private readonly IOmbiHttpClient _client; private readonly HttpClient _client;
private const string _changeLogUrl = "https://raw.githubusercontent.com/tidusjar/Ombi/{0}/CHANGELOG.md"; private const string _changeLogUrl = "https://raw.githubusercontent.com/tidusjar/Ombi/{0}/CHANGELOG.md";
private const string AppveyorApiUrl = "https://ci.appveyor.com/api"; private const string AppveyorApiUrl = "https://ci.appveyor.com/api";
private string ChangeLogUrl(string branch) => string.Format(_changeLogUrl, branch); private string ChangeLogUrl(string branch) => string.Format(_changeLogUrl, branch);
public async Task<UpdateModel> Process(string branch) public async Task<UpdateModel> Process()
{ {
var masterBranch = branch.Equals("master", StringComparison.CurrentCultureIgnoreCase);
string githubChangeLog;
githubChangeLog = await _client.GetStringAsync(new Uri(ChangeLogUrl(branch)));
var html = Markdown.ToHtml(githubChangeLog);
var doc = new HtmlDocument();
doc.LoadHtml(html);
HtmlNode latestRelease;
if (masterBranch)
{
latestRelease = doc.DocumentNode.Descendants("h2")
.FirstOrDefault(x => x.InnerText != "(unreleased)");
}
else
{
latestRelease = doc.DocumentNode.Descendants("h2")
.FirstOrDefault(x => x.InnerText == "(unreleased)");
if (latestRelease == null)
{
latestRelease = doc.DocumentNode.Descendants("h2")
.FirstOrDefault(x => x.InnerText != "(unreleased)");
}
}
var newFeatureList = latestRelease.NextSibling.NextSibling.NextSibling.NextSibling;
var featuresString = newFeatureList.ChildNodes.Where(x => x.Name != "#text").Select(x => x.InnerText.Replace("\\n", "")).ToList();
var fixes = newFeatureList.NextSibling.NextSibling.NextSibling.NextSibling;
var fixesString = fixes.ChildNodes.Where(x => x.Name != "#text").Select(x => x.InnerText.Replace("\\n", "")).ToList();
// Cleanup
var featuresList = featuresString.Distinct().ToList();
var fixesList = fixesString.Distinct().ToList();
// Get release
var release = new Release var release = new Release
{ {
Version = latestRelease.InnerText,
Features = featuresList,
Fixes = fixesList,
Downloads = new List<Downloads>() Downloads = new List<Downloads>()
}; };
if (masterBranch) await GetGitubRelease(release);
{
var releaseTag = latestRelease.InnerText.Substring(0, 9); return TransformUpdate(release);
await GetGitubRelease(release, releaseTag);
}
else
{
// Get AppVeyor
await GetAppVeyorRelease(release, branch);
}
return TransformUpdate(release,!masterBranch);
} }
private UpdateModel TransformUpdate(Release release, bool develop) private UpdateModel TransformUpdate(Release release)
{ {
var newUpdate = new UpdateModel var newUpdate = new UpdateModel
{ {
UpdateVersionString = develop ? release.Version : release.Version.Substring(1,8), UpdateVersionString = release.Version,
UpdateVersion = release.Version == "(unreleased)" ? 0 : int.Parse(release.Version.Substring(1, 5).Replace(".", "")), UpdateVersion = int.Parse(release.Version.Substring(1, 5).Replace(".", "")),
UpdateDate = DateTime.Now, UpdateDate = DateTime.Now,
ChangeLogs = new List<ChangeLog>(), ChangeLogs = release.Description,
Downloads = new List<Downloads>() Downloads = new List<Downloads>(),
}; UpdateAvailable = release.Version != AssemblyHelper.GetRuntimeVersion()
};
foreach (var dl in release.Downloads) foreach (var dl in release.Downloads)
{ {
@ -114,75 +61,16 @@ namespace Ombi.Schedule.Processor
}); });
} }
foreach (var f in release.Features)
{
var change = new ChangeLog
{
Descripion = f,
Type = "New",
};
newUpdate.ChangeLogs.Add(change);
}
foreach (var f in release.Fixes)
{
var change = new ChangeLog
{
Descripion = f,
Type = "Fixed",
};
newUpdate.ChangeLogs.Add(change);
}
return newUpdate; return newUpdate;
} }
private async Task GetAppVeyorRelease(Release release, string branch) private async Task GetGitubRelease(Release release)
{ {
var request = new Request($"/projects/tidusjar/requestplex/branch/{branch}", AppVeyorApi.AppveyorApiUrl, HttpMethod.Get); var client = new GitHubClient(Octokit.ProductHeaderValue.Parse("OmbiV4"));
request.ApplicationJsonContentType();
var builds = await _api.Request<AppveyorBranchResult>(request); var releases = await client.Repository.Release.GetAll("ombi-app", "ombi");
var jobId = builds.build.jobs.FirstOrDefault()?.jobId ?? string.Empty; var latest = releases.OrderByDescending(x => x.CreatedAt).FirstOrDefault();
if (builds.build.finished == DateTime.MinValue || builds.build.status.Equals("failed"))
{
return;
}
release.Version = builds.build.version;
// get the artifacts
request = new Request($"/buildjobs/{jobId}/artifacts", AppVeyorApi.AppveyorApiUrl, HttpMethod.Get);
request.ApplicationJsonContentType();
var artifacts = await _api.Request<List<BuildArtifacts>>(request);
foreach (var item in artifacts)
{
var d = new Downloads
{
Name = item.fileName,
Url = $"{AppveyorApiUrl}/buildjobs/{jobId}/artifacts/{item.fileName}"
};
release.Downloads.Add(d);
}
}
private async Task GetGitubRelease(Release release, string releaseTag)
{
var client = new GitHubClient(Octokit.ProductHeaderValue.Parse("OmbiV3"));
var releases = await client.Repository.Release.GetAll("tidusjar", "ombi");
var latest = releases.FirstOrDefault(x => x.TagName.Equals(releaseTag, StringComparison.InvariantCultureIgnoreCase));
if (latest.Name.Contains("V2", CompareOptions.IgnoreCase))
{
latest = null;
}
if (latest == null)
{
latest = releases.OrderByDescending(x => x.CreatedAt).FirstOrDefault();
}
foreach (var item in latest.Assets) foreach (var item in latest.Assets)
{ {
var d = new Downloads var d = new Downloads
@ -192,6 +80,8 @@ namespace Ombi.Schedule.Processor
}; };
release.Downloads.Add(d); release.Downloads.Add(d);
} }
release.Description = Markdown.ToHtml(latest.Body);
release.Version = latest.TagName;
} }
} }
public class Release public class Release
@ -199,8 +89,7 @@ namespace Ombi.Schedule.Processor
public string Version { get; set; } public string Version { get; set; }
public string CheckinVersion { get; set; } public string CheckinVersion { get; set; }
public List<Downloads> Downloads { get; set; } public List<Downloads> Downloads { get; set; }
public List<string> Features { get; set; } public string Description { get; set; }
public List<string> Fixes { get; set; }
} }
public class Downloads public class Downloads

View file

@ -4,6 +4,6 @@ namespace Ombi.Core.Processor
{ {
public interface IChangeLogProcessor public interface IChangeLogProcessor
{ {
Task<UpdateModel> Process(string branch); Task<UpdateModel> Process();
} }
} }

View file

@ -6,7 +6,6 @@
public bool CollectAnalyticData { get; set; } public bool CollectAnalyticData { get; set; }
public bool Wizard { get; set; } public bool Wizard { get; set; }
public string ApiKey { get; set; } public string ApiKey { get; set; }
public bool IgnoreCertificateErrors { get; set; }
public bool DoNotSendNotificationsForAutoApprove { get; set; } public bool DoNotSendNotificationsForAutoApprove { get; set; }
public bool HideRequestsUsers { get; set; } public bool HideRequestsUsers { get; set; }
public bool DisableHealthChecks { get; set; } public bool DisableHealthChecks { get; set; }

View file

@ -12,7 +12,6 @@ export interface IOmbiSettings extends ISettings {
collectAnalyticData: boolean; collectAnalyticData: boolean;
wizard: boolean; wizard: boolean;
apiKey: string; apiKey: string;
ignoreCertificateErrors: boolean;
doNotSendNotificationsForAutoApprove: boolean; doNotSendNotificationsForAutoApprove: boolean;
hideRequestsUsers: boolean; hideRequestsUsers: boolean;
defaultLanguageCode: string; defaultLanguageCode: string;
@ -285,3 +284,19 @@ export interface ITheMovieDbSettings extends ISettings {
showAdultMovies: boolean; showAdultMovies: boolean;
excludedKeywordIds: number[]; excludedKeywordIds: number[];
} }
export interface IUpdateModel
{
updateVersionString: string;
updateVersion: number;
updateDate: Date,
updateAvailable: boolean;
changeLogs: string;
downloads: IUpdateDonloads[];
}
export interface IUpdateDonloads
{
name: string;
url: string
}

View file

@ -0,0 +1,18 @@
import { PlatformLocation, APP_BASE_HREF } from "@angular/common";
import { Injectable, Inject } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { Observable } from "rxjs";
import { ServiceHelpers } from "./service.helpers";
import { IUpdateModel } from "../interfaces";
@Injectable()
export class UpdateService extends ServiceHelpers {
constructor(http: HttpClient, @Inject(APP_BASE_HREF) href:string) {
super(http, "/api/v1/Update/", href);
}
public checkForUpdate(): Observable<IUpdateModel> {
return this.http.get<IUpdateModel>(`${this.url}`, {headers: this.headers});
}
}

View file

@ -13,8 +13,8 @@
</div> </div>
<div class="mat-row"> <div class="mat-row">
<div class="mat-cell">Version</div> <div class="mat-cell">Version</div>
<div class="mat-cell">{{about.version}} <a [routerLink]="['/Settings/Update']" *ngIf="newUpdate" <div class="mat-cell">{{about.version}} <a (click)="openUpdate()" *ngIf="newUpdate"
style="color:#df691a"><b>(New Update Available)</b></a></div> style="color:#df691a; text-decoration: underline; cursor: pointer;"><b><i class="fas fa-code-branch"></i> (New Update Available)</b></a></div>
</div> </div>
<!-- <div class="mat-row"> <!-- <div class="mat-row">

View file

@ -1,7 +1,10 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { IAbout } from "../../interfaces/ISettings"; import { IAbout, IUpdateModel } from "../../interfaces/ISettings";
import { JobService, SettingsService, HubService, SystemService } from "../../services"; import { SettingsService, HubService, SystemService } from "../../services";
import { IConnectedUser } from "../../interfaces"; import { IConnectedUser } from "../../interfaces";
import { UpdateService } from "../../services/update.service";
import { MatDialog } from "@angular/material/dialog";
import { UpdateDialogComponent } from "./update-dialog.component";
@Component({ @Component({
templateUrl: "./about.component.html", templateUrl: "./about.component.html",
@ -14,22 +17,29 @@ export class AboutComponent implements OnInit {
public connectedUsers: IConnectedUser[]; public connectedUsers: IConnectedUser[];
public newsHtml: string; public newsHtml: string;
private update: IUpdateModel;
constructor(private readonly settingsService: SettingsService, constructor(private readonly settingsService: SettingsService,
private readonly jobService: JobService, private readonly jobService: UpdateService,
private readonly hubService: HubService, private readonly hubService: HubService,
private readonly systemService: SystemService) { } private readonly systemService: SystemService,
private readonly dialog: MatDialog) { }
public async ngOnInit() { public async ngOnInit() {
this.settingsService.about().subscribe(x => this.about = x); this.settingsService.about().subscribe(x => this.about = x);
this.newsHtml = await this.systemService.getNews().toPromise(); this.newsHtml = await this.systemService.getNews().toPromise();
// TODO this.jobService.checkForUpdate().subscribe(x => {
// this.jobService.getCachedUpdate().subscribe(x => { this.update = x;
// if (x === true) { if (x.updateAvailable) {
// // this.newUpdate = true; // TODO this.newUpdate = true;
// } }
// }); });
this.connectedUsers = await this.hubService.getConnectedUsers(); this.connectedUsers = await this.hubService.getConnectedUsers();
} }
public openUpdate() {
this.dialog.open(UpdateDialogComponent, { width: "700px", data: this.update, panelClass: 'modal-panel' });
}
} }

View file

@ -0,0 +1,28 @@
<h1 mat-dialog-title><i class="fas fa-code-branch"></i> Latest Version: {{data.updateVersionString}}</h1>
<mat-dialog-content>
<div [innerHTML]="data.changeLogs">
</div>
<div class="mat-table">
<div class="mat-header-row">
<div class="mat-header-cell">Binary</div>
<div class="mat-header-cell">Download</div></div>
<div *ngFor="let d of data.downloads" class="mat-row" >
<div class="mat-cell">{{d.name}}</div>
<div class="mat-cell"><a href="{{d.url}}">Download</a></div>
</div>
</div>
<small>Updated at {{data.updateDate | date}}</small>
</mat-dialog-content>
<div mat-dialog-actions class="right-buttons">
<button mat-raised-button id="cancelButton" [mat-dialog-close]="" color="warn"><i class="fas fa-times"></i> Close</button>
</div>

View file

@ -0,0 +1,40 @@
.mat-table {
display: block;
}
.mat-row,
.mat-header-row {
display: flex;
border-bottom-width: 1px;
border-bottom-style: solid;
align-items: center;
min-height: 48px;
padding: 0 24px;
}
.mat-cell,
.mat-header-cell {
flex: 1;
overflow: hidden;
word-wrap: break-word;
}
.small-middle-container{
margin: auto;
width: 85%;
margin-top:10px;
}
:host ::ng-deep strong {
color: #fff;
background-color: #007bff;
display: inline-block;
padding: 0.25em 0.4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
}

View file

@ -0,0 +1,16 @@
import { Component, OnInit, Inject } from "@angular/core";
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
import { IUpdateModel } from "../../interfaces";
@Component({
templateUrl: "update-dialog.component.html",
styleUrls: [ "update-dialog.component.scss" ]
})
export class UpdateDialogComponent {
constructor(
public dialogRef: MatDialogRef<UpdateDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: IUpdateModel
) { }
}

View file

@ -41,11 +41,6 @@
<input matInput formControlName="autoDeleteAfterDays"> <input matInput formControlName="autoDeleteAfterDays">
</mat-form-field> </mat-form-field>
</div> </div>
<div>
<mat-slide-toggle formControlName="ignoreCertificateErrors" matTooltip="Enable if you are having connectivity problems over SSL">
Ignore any certificate errors (Please restart after changing)
</mat-slide-toggle>
</div>
<div> <div>
<mat-slide-toggle formControlName="collectAnalyticData" matTooltip="This will allow us to have a better understanding of the userbase so we know what we should be supporting (Uses Google Analytics)"> <mat-slide-toggle formControlName="collectAnalyticData" matTooltip="This will allow us to have a better understanding of the userbase so we know what we should be supporting (Uses Google Analytics)">
Allow us to collect anonymous analytical data e.g. browser used Allow us to collect anonymous analytical data e.g. browser used

View file

@ -25,7 +25,6 @@ export class OmbiComponent implements OnInit {
this.form = this.fb.group({ this.form = this.fb.group({
collectAnalyticData: [x.collectAnalyticData], collectAnalyticData: [x.collectAnalyticData],
apiKey: [x.apiKey], apiKey: [x.apiKey],
ignoreCertificateErrors: [x.ignoreCertificateErrors],
baseUrl: [x.baseUrl], baseUrl: [x.baseUrl],
doNotSendNotificationsForAutoApprove: [x.doNotSendNotificationsForAutoApprove], doNotSendNotificationsForAutoApprove: [x.doNotSendNotificationsForAutoApprove],
hideRequestsUsers: [x.hideRequestsUsers], hideRequestsUsers: [x.hideRequestsUsers],

View file

@ -68,6 +68,9 @@ import { TwilioComponent } from "./notifications/twilio/twilio.component";
import { WhatsAppComponent } from "./notifications/twilio/whatsapp.component"; import { WhatsAppComponent } from "./notifications/twilio/whatsapp.component";
import { CloudMobileComponent } from "./notifications/cloudmobile.coponent"; import { CloudMobileComponent } from "./notifications/cloudmobile.coponent";
import { CloudMobileService } from "../services/cloudmobile.service"; import { CloudMobileService } from "../services/cloudmobile.service";
import { UpdateService } from "../services/update.service";
import { MatDialogModule } from "@angular/material/dialog";
import { UpdateDialogComponent } from "./about/update-dialog.component";
const routes: Routes = [ const routes: Routes = [
{ path: "Ombi", component: OmbiComponent, canActivate: [AuthGuard] }, { path: "Ombi", component: OmbiComponent, canActivate: [AuthGuard] },
@ -126,7 +129,8 @@ const routes: Routes = [
RadioButtonModule, RadioButtonModule,
DialogModule, DialogModule,
SharedModule, SharedModule,
MatMenuModule MatMenuModule,
MatDialogModule
], ],
declarations: [ declarations: [
SettingsMenuComponent, SettingsMenuComponent,
@ -169,6 +173,7 @@ const routes: Routes = [
TwilioComponent, TwilioComponent,
WhatsAppComponent, WhatsAppComponent,
CloudMobileComponent, CloudMobileComponent,
UpdateDialogComponent,
], ],
exports: [ exports: [
RouterModule, RouterModule,
@ -195,6 +200,7 @@ const routes: Routes = [
FileDownloadService, FileDownloadService,
TheMovieDbService, TheMovieDbService,
CloudMobileService, CloudMobileService,
UpdateService,
], ],
}) })

View file

@ -52,8 +52,7 @@ namespace Ombi.Controllers.V1
{ {
var productArray = _updater.GetVersion(); var productArray = _updater.GetVersion();
var version = productArray[0]; var version = productArray[0];
var branch = productArray[1]; var updateAvailable = await _updater.UpdateAvailable(version);
var updateAvailable = await _updater.UpdateAvailable(branch, version);
return updateAvailable; return updateAvailable;
} }
@ -70,12 +69,11 @@ namespace Ombi.Controllers.V1
var val = await _memCache.GetOrAdd(CacheKeys.Update, async () => var val = await _memCache.GetOrAdd(CacheKeys.Update, async () =>
{ {
var productArray = _updater.GetVersion(); var productArray = _updater.GetVersion();
if (productArray.Length > 1)
{
var version = productArray[0]; var version = productArray[0];
var branch = productArray[1];
var updateAvailable = await _updater.UpdateAvailable(branch, version); var updateAvailable = await _updater.UpdateAvailable( version);
}
return true; return true;
}); });

View file

@ -21,10 +21,10 @@ namespace Ombi.Controllers.V1
private readonly ICacheService _cache; private readonly ICacheService _cache;
private readonly IChangeLogProcessor _processor; private readonly IChangeLogProcessor _processor;
[HttpGet("{branch}")] [HttpGet()]
public async Task<UpdateModel> UpdateAvailable(string branch) public async Task<UpdateModel> UpdateAvailable()
{ {
return await _cache.GetOrAdd(branch, async () => await _processor.Process(branch)); return await _cache.GetOrAdd("Update", async () => await _processor.Process());
} }
} }
} }

View file

@ -63,6 +63,8 @@
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="5.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="2.2.0" /> <PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="2.2.0" />
<PackageReference Include="ncrontab" Version="3.3.0" /> <PackageReference Include="ncrontab" Version="3.3.0" />

View file

@ -15,12 +15,11 @@ using System.Threading.Tasks;
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json; using Newtonsoft.Json;
using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models;
using System.Diagnostics;
using System.IO; using System.IO;
using Ombi.Api.TheMovieDb;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Ombi.Api.TheMovieDb.Models; using Ombi.Api.TheMovieDb.Models;
using System.Net.Http;
namespace Ombi namespace Ombi
{ {
@ -131,8 +130,8 @@ namespace Ombi
} }
await SortOutBaseUrl(baseUrl, settingsDb, ombiSettingsContent); await SortOutBaseUrl(baseUrl, settingsDb, ombiSettingsContent);
var httpClient = new Ombi.Api.OmbiHttpClient(null, null); var httpClient = new HttpClient();
var api = new Ombi.Api.Api(new Logger<Api.Api>(NullLoggerFactory.Instance), httpClient); var api = new Ombi.Api.Api(new Logger<Api.Api>(NullLoggerFactory.Instance), httpClient);
await MigrateOldTvDbIds(ombiDb, ombiSettingsContent, settingsDb, new Ombi.Api.TheMovieDb.TheMovieDbApi(null, (Api.IApi)api, null)); await MigrateOldTvDbIds(ombiDb, ombiSettingsContent, settingsDb, new Ombi.Api.TheMovieDb.TheMovieDbApi(null, (Api.IApi)api, null));
Console.WriteLine($"We are running on {urlValue}"); Console.WriteLine($"We are running on {urlValue}");

14
src/Ombi/databadse.json Normal file
View file

@ -0,0 +1,14 @@
{
"OmbiDatabase": {
"Type": "MySQL",
"ConnectionString": "Server=192.168.68.118;Port=3306;Database=app.ombi.io;User=ombi"
},
"SettingsDatabase": {
"Type": "MySQL",
"ConnectionString": "Server=192.168.68.118;Port=3306;Database=app.ombi.io;User=ombi"
},
"ExternalDatabase": {
"Type": "MySQL",
"ConnectionString": "Server=192.168.68.118;Port=3306;Database=app.ombi.io;User=ombi"
}
}