New: App health displayed in UI
This commit is contained in:
parent
90a6bcaa47
commit
c8ae9f40fb
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Datastore.Events;
|
||||
using NzbDrone.Core.HealthCheck;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
||||
namespace NzbDrone.Api.Health
|
||||
{
|
||||
public class HealthModule : NzbDroneRestModuleWithSignalR<HealthResource, HealthCheck>,
|
||||
IHandle<TriggerHealthCheckEvent>
|
||||
{
|
||||
private readonly IHealthCheckService _healthCheckService;
|
||||
|
||||
public HealthModule(ICommandExecutor commandExecutor, IHealthCheckService healthCheckService)
|
||||
: base(commandExecutor)
|
||||
{
|
||||
_healthCheckService = healthCheckService;
|
||||
GetResourceAll = GetHealth;
|
||||
}
|
||||
|
||||
private List<HealthResource> GetHealth()
|
||||
{
|
||||
return ToListResource(_healthCheckService.PerformHealthCheck);
|
||||
}
|
||||
|
||||
public void Handle(TriggerHealthCheckEvent message)
|
||||
{
|
||||
BroadcastResourceChange(ModelAction.Sync);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Core.HealthCheck;
|
||||
|
||||
|
||||
namespace NzbDrone.Api.Health
|
||||
{
|
||||
public class HealthResource : RestResource
|
||||
{
|
||||
public HealthCheckResultType Type { get; set; }
|
||||
public String Message { get; set; }
|
||||
}
|
||||
}
|
|
@ -132,6 +132,8 @@
|
|||
<Compile Include="Frontend\Mappers\IMapHttpRequestsToDisk.cs" />
|
||||
<Compile Include="Frontend\Mappers\StaticResourceMapperBase.cs" />
|
||||
<Compile Include="Frontend\StaticResourceModule.cs" />
|
||||
<Compile Include="Health\HistoryResource.cs" />
|
||||
<Compile Include="Health\HealthModule.cs" />
|
||||
<Compile Include="History\HistoryResource.cs" />
|
||||
<Compile Include="History\HistoryModule.cs" />
|
||||
<Compile Include="Metadata\MetadataResource.cs" />
|
||||
|
|
|
@ -92,7 +92,7 @@ namespace NzbDrone.Automation.Test.PageModel
|
|||
{
|
||||
get
|
||||
{
|
||||
return Find(By.LinkText("System"));
|
||||
return Find(By.PartialLinkText("System"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,5 +52,10 @@ namespace NzbDrone.Common
|
|||
{
|
||||
return CollapseSpace.Replace(text, " ").Trim();
|
||||
}
|
||||
|
||||
public static bool IsNullOrWhiteSpace(this string text)
|
||||
{
|
||||
return String.IsNullOrWhiteSpace(text);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.HealthCheck.Checks;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class DownloadClientCheckFixture : CoreTest<DownloadClientCheck>
|
||||
{
|
||||
[Test]
|
||||
public void should_return_warning_when_download_client_has_not_been_configured()
|
||||
{
|
||||
Mocker.GetMock<IProvideDownloadClient>()
|
||||
.Setup(s => s.GetDownloadClient())
|
||||
.Returns((IDownloadClient)null);
|
||||
|
||||
Subject.Check().ShouldBeWarning();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_error_when_download_client_throws()
|
||||
{
|
||||
var downloadClient = Mocker.GetMock<IDownloadClient>();
|
||||
|
||||
downloadClient.Setup(s => s.GetQueue())
|
||||
.Throws<Exception>();
|
||||
|
||||
Mocker.GetMock<IProvideDownloadClient>()
|
||||
.Setup(s => s.GetDownloadClient())
|
||||
.Returns(downloadClient.Object);
|
||||
|
||||
Subject.Check().ShouldBeError();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_null_when_download_client_returns()
|
||||
{
|
||||
var downloadClient = Mocker.GetMock<IDownloadClient>();
|
||||
|
||||
downloadClient.Setup(s => s.GetQueue())
|
||||
.Returns(new List<QueueItem>());
|
||||
|
||||
Mocker.GetMock<IProvideDownloadClient>()
|
||||
.Setup(s => s.GetDownloadClient())
|
||||
.Returns(downloadClient.Object);
|
||||
|
||||
Subject.Check().Should().BeNull();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.HealthCheck.Checks;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class DroneFactoryCheckFixture : CoreTest<DroneFactoryCheck>
|
||||
{
|
||||
private const string DRONE_FACTORY_FOLDER = @"C:\Test\Unsorted";
|
||||
|
||||
private void GivenDroneFactoryFolder(bool exists = false)
|
||||
{
|
||||
Mocker.GetMock<IConfigService>()
|
||||
.SetupGet(s => s.DownloadedEpisodesFolder)
|
||||
.Returns(DRONE_FACTORY_FOLDER);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(s => s.FolderExists(DRONE_FACTORY_FOLDER))
|
||||
.Returns(exists);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_warning_when_drone_factory_folder_is_not_configured()
|
||||
{
|
||||
Mocker.GetMock<IConfigService>()
|
||||
.SetupGet(s => s.DownloadedEpisodesFolder)
|
||||
.Returns("");
|
||||
|
||||
Subject.Check().ShouldBeWarning();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_error_when_drone_factory_folder_does_not_exist()
|
||||
{
|
||||
GivenDroneFactoryFolder();
|
||||
|
||||
Subject.Check().ShouldBeError();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_error_when_unable_to_write_to_drone_factory_folder()
|
||||
{
|
||||
GivenDroneFactoryFolder(true);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(s => s.WriteAllText(It.IsAny<String>(), It.IsAny<String>()))
|
||||
.Throws<Exception>();
|
||||
|
||||
Subject.Check().ShouldBeError();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_null_when_no_issues_found()
|
||||
{
|
||||
GivenDroneFactoryFolder(true);
|
||||
|
||||
Subject.Check().Should().BeNull();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using FluentAssertions;
|
||||
using NzbDrone.Core.HealthCheck;
|
||||
|
||||
namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
{
|
||||
public static class HealthCheckFixtureExtensions
|
||||
{
|
||||
public static void ShouldBeWarning(this Core.HealthCheck.HealthCheck result)
|
||||
{
|
||||
result.Type.Should().Be(HealthCheckResultType.Warning);
|
||||
}
|
||||
|
||||
public static void ShouldBeError(this Core.HealthCheck.HealthCheck result)
|
||||
{
|
||||
result.Type.Should().Be(HealthCheckResultType.Error);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.HealthCheck.Checks;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class IndexerCheckFixture : CoreTest<IndexerCheck>
|
||||
{
|
||||
[Test]
|
||||
public void should_return_error_when_not_indexers_are_enabled()
|
||||
{
|
||||
Mocker.GetMock<IIndexerFactory>()
|
||||
.Setup(s => s.GetAvailableProviders())
|
||||
.Returns(new List<IIndexer>());
|
||||
|
||||
Subject.Check().ShouldBeError();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_warning_when_only_enabled_indexer_is_wombles()
|
||||
{
|
||||
var indexer = Mocker.GetMock<IIndexer>();
|
||||
indexer.SetupGet(s => s.SupportsSearching).Returns(false);
|
||||
|
||||
Mocker.GetMock<IIndexerFactory>()
|
||||
.Setup(s => s.GetAvailableProviders())
|
||||
.Returns(new List<IIndexer>{indexer.Object});
|
||||
|
||||
Subject.Check().ShouldBeWarning();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_null_when_multiple_multiple_indexers_are_enabled()
|
||||
{
|
||||
var indexers = new List<IIndexer>{Mocker.GetMock<IIndexer>().Object, Mocker.GetMock<IIndexer>().Object};
|
||||
|
||||
Mocker.GetMock<IIndexerFactory>()
|
||||
.Setup(s => s.GetAvailableProviders())
|
||||
.Returns(indexers);
|
||||
|
||||
Subject.Check().Should().BeNull();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.HealthCheck.Checks;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class UpdateCheckFixture : CoreTest<UpdateCheck>
|
||||
{
|
||||
[Test]
|
||||
public void should_return_error_when_app_folder_is_write_protected()
|
||||
{
|
||||
WindowsOnly();
|
||||
|
||||
Mocker.GetMock<IAppFolderInfo>()
|
||||
.Setup(s => s.StartUpFolder)
|
||||
.Returns(@"C:\NzbDrone");
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(s => s.WriteAllText(It.IsAny<String>(), It.IsAny<String>()))
|
||||
.Throws<Exception>();
|
||||
|
||||
Subject.Check().ShouldBeError();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -125,6 +125,11 @@
|
|||
<Compile Include="Framework\CoreTest.cs" />
|
||||
<Compile Include="Framework\DbTest.cs" />
|
||||
<Compile Include="Framework\NBuilderExtensions.cs" />
|
||||
<Compile Include="HealthCheck\Checks\UpdateCheckFixture.cs" />
|
||||
<Compile Include="HealthCheck\Checks\IndexerCheckFixture.cs" />
|
||||
<Compile Include="HealthCheck\Checks\DroneFactoryCheckFixture.cs" />
|
||||
<Compile Include="HealthCheck\Checks\DownloadClientCheckFixture.cs" />
|
||||
<Compile Include="HealthCheck\Checks\HealthCheckFixtureExtentions.cs" />
|
||||
<Compile Include="HistoryTests\HistoryServiceFixture.cs" />
|
||||
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedHistoryItemsFixture.cs" />
|
||||
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedEpisodeFilesFixture.cs" />
|
||||
|
|
|
@ -58,11 +58,21 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
|||
{
|
||||
}
|
||||
|
||||
public void Execute(TestBlackholeCommand message)
|
||||
public override void Test()
|
||||
{
|
||||
var testPath = Path.Combine(message.Folder, "drone_test.txt");
|
||||
PerformTest(Settings.Folder);
|
||||
}
|
||||
|
||||
private void PerformTest(string folder)
|
||||
{
|
||||
var testPath = Path.Combine(folder, "drone_test.txt");
|
||||
_diskProvider.WriteAllText(testPath, DateTime.Now.ToString());
|
||||
_diskProvider.DeleteFile(testPath);
|
||||
}
|
||||
|
||||
public void Execute(TestBlackholeCommand message)
|
||||
{
|
||||
PerformTest(message.Folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,12 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public VersionResponse GetVersion(string host = null, int port = 0, string username = null, string password = null)
|
||||
public override void Test()
|
||||
{
|
||||
_proxy.GetVersion(Settings);
|
||||
}
|
||||
|
||||
private VersionResponse GetVersion(string host = null, int port = 0, string username = null, string password = null)
|
||||
{
|
||||
return _proxy.GetVersion(Settings);
|
||||
}
|
||||
|
|
|
@ -80,11 +80,21 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
|
|||
{
|
||||
}
|
||||
|
||||
public void Execute(TestPneumaticCommand message)
|
||||
public override void Test()
|
||||
{
|
||||
var testPath = Path.Combine(message.Folder, "drone_test.txt");
|
||||
PerformTest(Settings.Folder);
|
||||
}
|
||||
|
||||
private void PerformTest(string folder)
|
||||
{
|
||||
var testPath = Path.Combine(folder, "drone_test.txt");
|
||||
_diskProvider.WriteAllText(testPath, DateTime.Now.ToString());
|
||||
_diskProvider.DeleteFile(testPath);
|
||||
}
|
||||
|
||||
public void Execute(TestPneumaticCommand message)
|
||||
{
|
||||
PerformTest(message.Folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,6 +122,11 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
|||
_sabnzbdProxy.RemoveFrom("history", id, Settings);
|
||||
}
|
||||
|
||||
public override void Test()
|
||||
{
|
||||
_sabnzbdProxy.GetCategories(Settings);
|
||||
}
|
||||
|
||||
public void Execute(TestSabnzbdCommand message)
|
||||
{
|
||||
var settings = new SabnzbdSettings();
|
||||
|
|
|
@ -44,5 +44,6 @@ namespace NzbDrone.Core.Download
|
|||
public abstract IEnumerable<HistoryItem> GetHistory(int start = 0, int limit = 10);
|
||||
public abstract void RemoveFromQueue(string id);
|
||||
public abstract void RemoveFromHistory(string id);
|
||||
public abstract void Test();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Composition;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
|
@ -15,8 +16,8 @@ namespace NzbDrone.Core.Download
|
|||
{
|
||||
private readonly IDownloadClientRepository _providerRepository;
|
||||
|
||||
public DownloadClientFactory(IDownloadClientRepository providerRepository, IEnumerable<IDownloadClient> providers, IContainer container, Logger logger)
|
||||
: base(providerRepository, providers, container, logger)
|
||||
public DownloadClientFactory(IDownloadClientRepository providerRepository, IEnumerable<IDownloadClient> providers, IContainer container, IEventAggregator eventAggregator, Logger logger)
|
||||
: base(providerRepository, providers, container, eventAggregator, logger)
|
||||
{
|
||||
_providerRepository = providerRepository;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Download.Clients;
|
||||
using NzbDrone.Core.Download.Clients.Nzbget;
|
||||
using NzbDrone.Core.Download.Clients.Sabnzbd;
|
||||
using System.Linq;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
{
|
||||
|
|
|
@ -11,5 +11,6 @@ namespace NzbDrone.Core.Download
|
|||
IEnumerable<HistoryItem> GetHistory(int start = 0, int limit = 0);
|
||||
void RemoveFromQueue(string id);
|
||||
void RemoveFromHistory(string id);
|
||||
void Test();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
using NzbDrone.Core.Messaging.Commands;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck
|
||||
{
|
||||
public class CheckHealthCommand : Command
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using NzbDrone.Core.Download;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
public class DownloadClientCheck : IProvideHealthCheck
|
||||
{
|
||||
private readonly IProvideDownloadClient _downloadClientProvider;
|
||||
|
||||
public DownloadClientCheck(IProvideDownloadClient downloadClientProvider)
|
||||
{
|
||||
_downloadClientProvider = downloadClientProvider;
|
||||
}
|
||||
|
||||
public HealthCheck Check()
|
||||
{
|
||||
var downloadClient = _downloadClientProvider.GetDownloadClient();
|
||||
|
||||
if (downloadClient == null)
|
||||
{
|
||||
return new HealthCheck(HealthCheckResultType.Warning, "No download client is available");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
downloadClient.GetQueue();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return new HealthCheck(HealthCheckResultType.Error, "Unable to communicate with download client");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
public class DroneFactoryCheck : IProvideHealthCheck
|
||||
{
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
|
||||
public DroneFactoryCheck(IConfigService configService, IDiskProvider diskProvider)
|
||||
{
|
||||
_configService = configService;
|
||||
_diskProvider = diskProvider;
|
||||
}
|
||||
|
||||
public HealthCheck Check()
|
||||
{
|
||||
var droneFactoryFolder = _configService.DownloadedEpisodesFolder;
|
||||
|
||||
if (droneFactoryFolder.IsNullOrWhiteSpace())
|
||||
{
|
||||
return new HealthCheck(HealthCheckResultType.Warning, "Drone factory folder is not configured");
|
||||
}
|
||||
|
||||
if (!_diskProvider.FolderExists(droneFactoryFolder))
|
||||
{
|
||||
return new HealthCheck(HealthCheckResultType.Error, "Drone factory folder does not exist");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var testPath = Path.Combine(droneFactoryFolder, "drone_test.txt");
|
||||
_diskProvider.WriteAllText(testPath, DateTime.Now.ToString());
|
||||
_diskProvider.DeleteFile(testPath);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return new HealthCheck(HealthCheckResultType.Error, "Unable to write to drone factory folder");
|
||||
}
|
||||
|
||||
//Todo: Unable to import one or more files/folders from
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
using System.Linq;
|
||||
using NzbDrone.Core.Indexers;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
public class IndexerCheck : IProvideHealthCheck
|
||||
{
|
||||
private readonly IIndexerFactory _indexerFactory;
|
||||
|
||||
public IndexerCheck(IIndexerFactory indexerFactory)
|
||||
{
|
||||
_indexerFactory = indexerFactory;
|
||||
}
|
||||
|
||||
public HealthCheck Check()
|
||||
{
|
||||
var enabled = _indexerFactory.GetAvailableProviders();
|
||||
|
||||
if (!enabled.Any())
|
||||
{
|
||||
return new HealthCheck(HealthCheckResultType.Error, "No indexers are enabled");
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (enabled.All(i => i.SupportsSearching == false))
|
||||
{
|
||||
return new HealthCheck(HealthCheckResultType.Warning, "Enabled indexers do not support searching");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.Update;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
public class UpdateCheck : IProvideHealthCheck
|
||||
{
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IAppFolderInfo _appFolderInfo;
|
||||
private readonly ICheckUpdateService _checkUpdateService;
|
||||
|
||||
public UpdateCheck(IDiskProvider diskProvider, IAppFolderInfo appFolderInfo, ICheckUpdateService checkUpdateService)
|
||||
{
|
||||
_diskProvider = diskProvider;
|
||||
_appFolderInfo = appFolderInfo;
|
||||
_checkUpdateService = checkUpdateService;
|
||||
}
|
||||
|
||||
|
||||
public HealthCheck Check()
|
||||
{
|
||||
if (OsInfo.IsWindows)
|
||||
{
|
||||
try
|
||||
{
|
||||
var testPath = Path.Combine(_appFolderInfo.StartUpFolder, "drone_test.txt");
|
||||
_diskProvider.WriteAllText(testPath, DateTime.Now.ToString());
|
||||
_diskProvider.DeleteFile(testPath);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return new HealthCheck(HealthCheckResultType.Error,
|
||||
"Unable to update, running from write-protected folder");
|
||||
}
|
||||
}
|
||||
|
||||
if (BuildInfo.BuildDateTime < DateTime.UtcNow.AddDays(-14))
|
||||
{
|
||||
if (_checkUpdateService.AvailableUpdate() != null)
|
||||
{
|
||||
return new HealthCheck(HealthCheckResultType.Warning, "New update is available");
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck
|
||||
{
|
||||
public class HealthCheck : ModelBase
|
||||
{
|
||||
public HealthCheckResultType Type { get; set; }
|
||||
public String Message { get; set; }
|
||||
|
||||
public HealthCheck(HealthCheckResultType type, string message)
|
||||
{
|
||||
Type = type;
|
||||
Message = message;
|
||||
}
|
||||
}
|
||||
|
||||
public enum HealthCheckResultType
|
||||
{
|
||||
Warning = 1,
|
||||
Error = 2
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Configuration.Events;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.ThingiProvider.Events;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck
|
||||
{
|
||||
public interface IHealthCheckService
|
||||
{
|
||||
List<HealthCheck> PerformHealthCheck();
|
||||
}
|
||||
|
||||
public class HealthCheckService : IHealthCheckService,
|
||||
IExecute<CheckHealthCommand>,
|
||||
IHandleAsync<ConfigSavedEvent>,
|
||||
IHandleAsync<ProviderUpdatedEvent<IIndexer>>,
|
||||
IHandleAsync<ProviderUpdatedEvent<IDownloadClient>>
|
||||
{
|
||||
private readonly IEnumerable<IProvideHealthCheck> _healthChecks;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public HealthCheckService(IEnumerable<IProvideHealthCheck> healthChecks, IEventAggregator eventAggregator, Logger logger)
|
||||
{
|
||||
_healthChecks = healthChecks;
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<HealthCheck> PerformHealthCheck()
|
||||
{
|
||||
_logger.Trace("Checking health");
|
||||
var result = _healthChecks.Select(c => c.Check()).Where(c => c != null).ToList();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Execute(CheckHealthCommand message)
|
||||
{
|
||||
//Until we have stored health checks we should just trigger the complete event
|
||||
//and let the clients check in
|
||||
//Multiple connected clients means we're going to compute the health check multiple times
|
||||
//Multiple checks feels a bit ugly, but means the most up to date information goes to the client
|
||||
_eventAggregator.PublishEvent(new TriggerHealthCheckEvent());
|
||||
}
|
||||
|
||||
public void HandleAsync(ConfigSavedEvent message)
|
||||
{
|
||||
_eventAggregator.PublishEvent(new TriggerHealthCheckEvent());
|
||||
}
|
||||
|
||||
public void HandleAsync(ProviderUpdatedEvent<IIndexer> message)
|
||||
{
|
||||
_eventAggregator.PublishEvent(new TriggerHealthCheckEvent());
|
||||
}
|
||||
|
||||
public void HandleAsync(ProviderUpdatedEvent<IDownloadClient> message)
|
||||
{
|
||||
_eventAggregator.PublishEvent(new TriggerHealthCheckEvent());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace NzbDrone.Core.HealthCheck
|
||||
{
|
||||
public interface IProvideHealthCheck
|
||||
{
|
||||
HealthCheck Check();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
using NzbDrone.Common.Messaging;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck
|
||||
{
|
||||
public class TriggerHealthCheckEvent : IEvent
|
||||
{
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ namespace NzbDrone.Core.Indexers
|
|||
IParseFeed Parser { get; }
|
||||
DownloadProtocol Protocol { get; }
|
||||
Boolean SupportsPaging { get; }
|
||||
Boolean SupportsSearching { get; }
|
||||
|
||||
IEnumerable<string> RecentFeed { get; }
|
||||
IEnumerable<string> GetEpisodeSearchUrls(string seriesTitle, int tvRageId, int seasonNumber, int episodeNumber);
|
||||
|
|
|
@ -35,6 +35,7 @@ namespace NzbDrone.Core.Indexers
|
|||
public abstract DownloadProtocol Protocol { get; }
|
||||
|
||||
public abstract bool SupportsPaging { get; }
|
||||
public virtual bool SupportsSearching { get { return true; } }
|
||||
|
||||
protected TSettings Settings
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Composition;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Indexers
|
||||
|
@ -16,8 +17,8 @@ namespace NzbDrone.Core.Indexers
|
|||
private readonly IIndexerRepository _providerRepository;
|
||||
private readonly INewznabTestService _newznabTestService;
|
||||
|
||||
public IndexerFactory(IIndexerRepository providerRepository, IEnumerable<IIndexer> providers, IContainer container, INewznabTestService newznabTestService, Logger logger)
|
||||
: base(providerRepository, providers, container, logger)
|
||||
public IndexerFactory(IIndexerRepository providerRepository, IEnumerable<IIndexer> providers, IContainer container, IEventAggregator eventAggregator, INewznabTestService newznabTestService, Logger logger)
|
||||
: base(providerRepository, providers, container, eventAggregator, logger)
|
||||
{
|
||||
_providerRepository = providerRepository;
|
||||
_newznabTestService = newznabTestService;
|
||||
|
|
|
@ -22,6 +22,14 @@ namespace NzbDrone.Core.Indexers.Wombles
|
|||
}
|
||||
}
|
||||
|
||||
public override bool SupportsSearching
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override IParseFeed Parser
|
||||
{
|
||||
get
|
||||
|
|
|
@ -7,6 +7,7 @@ using NzbDrone.Core.Configuration.Events;
|
|||
using NzbDrone.Core.DataAugmentation.Scene;
|
||||
using NzbDrone.Core.DataAugmentation.Xem;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.HealthCheck;
|
||||
using NzbDrone.Core.Housekeeping;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Instrumentation.Commands;
|
||||
|
@ -50,6 +51,7 @@ namespace NzbDrone.Core.Jobs
|
|||
new ScheduledTask{ Interval = 1, TypeName = typeof(DownloadedEpisodesScanCommand).FullName},
|
||||
new ScheduledTask{ Interval = 1, TypeName = typeof(TrackedCommandCleanupCommand).FullName},
|
||||
new ScheduledTask{ Interval = 1, TypeName = typeof(CheckForFailedDownloadCommand).FullName},
|
||||
new ScheduledTask{ Interval = 5, TypeName = typeof(CheckHealthCommand).FullName},
|
||||
new ScheduledTask{ Interval = 1*60, TypeName = typeof(ApplicationUpdateCommand).FullName},
|
||||
new ScheduledTask{ Interval = 1*60, TypeName = typeof(TrimLogCommand).FullName},
|
||||
new ScheduledTask{ Interval = 3*60, TypeName = typeof(UpdateSceneMappingCommand).FullName},
|
||||
|
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Composition;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Metadata.Consumers.Fake;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
|
@ -17,8 +18,8 @@ namespace NzbDrone.Core.Metadata
|
|||
{
|
||||
private readonly IMetadataRepository _providerRepository;
|
||||
|
||||
public MetadataFactory(IMetadataRepository providerRepository, IEnumerable<IMetadata> providers, IContainer container, Logger logger)
|
||||
: base(providerRepository, providers, container, logger)
|
||||
public MetadataFactory(IMetadataRepository providerRepository, IEnumerable<IMetadata> providers, IContainer container, IEventAggregator eventAggregator, Logger logger)
|
||||
: base(providerRepository, providers, container, eventAggregator, logger)
|
||||
{
|
||||
_providerRepository = providerRepository;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Composition;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Notifications
|
||||
|
@ -15,8 +16,8 @@ namespace NzbDrone.Core.Notifications
|
|||
|
||||
public class NotificationFactory : ProviderFactory<INotification, NotificationDefinition>, INotificationFactory
|
||||
{
|
||||
public NotificationFactory(INotificationRepository providerRepository, IEnumerable<INotification> providers, IContainer container, Logger logger)
|
||||
: base(providerRepository, providers, container, logger)
|
||||
public NotificationFactory(INotificationRepository providerRepository, IEnumerable<INotification> providers, IContainer container, IEventAggregator eventAggregator, Logger logger)
|
||||
: base(providerRepository, providers, container, eventAggregator, logger)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -264,6 +264,13 @@
|
|||
<Compile Include="Exceptions\DownstreamException.cs" />
|
||||
<Compile Include="Exceptions\NzbDroneClientException.cs" />
|
||||
<Compile Include="Exceptions\StatusCodeToExceptions.cs" />
|
||||
<Compile Include="HealthCheck\CheckHealthCommand.cs" />
|
||||
<Compile Include="HealthCheck\Checks\DownloadClientCheck.cs" />
|
||||
<Compile Include="HealthCheck\Checks\DroneFactoryCheck.cs" />
|
||||
<Compile Include="HealthCheck\Checks\IndexerCheck.cs" />
|
||||
<Compile Include="HealthCheck\Checks\UpdateCheck.cs" />
|
||||
<Compile Include="HealthCheck\HealthCheck.cs" />
|
||||
<Compile Include="HealthCheck\TriggerHealthCheckEvent.cs" />
|
||||
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedEpisodes.cs" />
|
||||
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedHistoryItems.cs" />
|
||||
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedMetadataFiles.cs" />
|
||||
|
@ -342,6 +349,8 @@
|
|||
<Compile Include="Metadata\MetadataRepository.cs" />
|
||||
<Compile Include="Metadata\MetadataService.cs" />
|
||||
<Compile Include="Metadata\MetadataType.cs" />
|
||||
<Compile Include="HealthCheck\HealthCheckService.cs" />
|
||||
<Compile Include="HealthCheck\IProvideHealthCheck.cs" />
|
||||
<Compile Include="Notifications\NotificationFactory.cs" />
|
||||
<Compile Include="Notifications\NotificationService.cs" />
|
||||
<Compile Include="Notifications\DownloadMessage.cs" />
|
||||
|
@ -503,6 +512,7 @@
|
|||
<Compile Include="Rest\JsonNetSerializer.cs" />
|
||||
<Compile Include="RootFolders\RootFolderRepository.cs" />
|
||||
<Compile Include="ThingiProvider\ConfigContractNotFoundException.cs" />
|
||||
<Compile Include="ThingiProvider\Events\ProviderUpdatedEvent.cs" />
|
||||
<Compile Include="ThingiProvider\IProvider.cs" />
|
||||
<Compile Include="Qualities\QualityProfileInUseException.cs" />
|
||||
<Compile Include="Qualities\QualityDefinitionRepository.cs" />
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NzbDrone.Common.Messaging;
|
||||
|
||||
namespace NzbDrone.Core.ThingiProvider.Events
|
||||
{
|
||||
public class ProviderUpdatedEvent<TProvider> : IEvent
|
||||
{
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ using NLog;
|
|||
using NzbDrone.Common.Composition;
|
||||
using NzbDrone.Core.Lifecycle;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.ThingiProvider.Events;
|
||||
|
||||
namespace NzbDrone.Core.ThingiProvider
|
||||
{
|
||||
|
@ -14,6 +15,7 @@ namespace NzbDrone.Core.ThingiProvider
|
|||
{
|
||||
private readonly IProviderRepository<TProviderDefinition> _providerRepository;
|
||||
private readonly IContainer _container;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
protected readonly List<TProvider> _providers;
|
||||
|
@ -21,10 +23,12 @@ namespace NzbDrone.Core.ThingiProvider
|
|||
protected ProviderFactory(IProviderRepository<TProviderDefinition> providerRepository,
|
||||
IEnumerable<TProvider> providers,
|
||||
IContainer container,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
{
|
||||
_providerRepository = providerRepository;
|
||||
_container = container;
|
||||
_eventAggregator = eventAggregator;
|
||||
_providers = providers.ToList();
|
||||
_logger = logger;
|
||||
}
|
||||
|
@ -62,6 +66,7 @@ namespace NzbDrone.Core.ThingiProvider
|
|||
public virtual void Update(TProviderDefinition definition)
|
||||
{
|
||||
_providerRepository.Update(definition);
|
||||
_eventAggregator.PublishEvent(new ProviderUpdatedEvent<TProvider>());
|
||||
}
|
||||
|
||||
public void Delete(int id)
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
|
||||
.icon-nd-warning:before {
|
||||
.icon(@warning-sign);
|
||||
color : #f89406;
|
||||
color : @orange;
|
||||
}
|
||||
|
||||
.icon-nd-edit:before {
|
||||
|
@ -79,7 +79,7 @@
|
|||
|
||||
.icon-nd-form-warning:before {
|
||||
.icon(@warning-sign);
|
||||
color: #f89406;
|
||||
color: @orange;
|
||||
}
|
||||
|
||||
.icon-nd-form-danger:before {
|
||||
|
@ -176,3 +176,13 @@
|
|||
.icon-nd-restart:before {
|
||||
.icon(@repeat);
|
||||
}
|
||||
|
||||
.icon-nd-health-warning:before {
|
||||
.icon(@exclamation-sign);
|
||||
color : @orange
|
||||
}
|
||||
|
||||
.icon-nd-health-error:before {
|
||||
.icon(@exclamation-sign);
|
||||
color : @errorText
|
||||
}
|
|
@ -22,6 +22,8 @@
|
|||
li {
|
||||
list-style-type : none;
|
||||
display : inline-block;
|
||||
position : relative;
|
||||
|
||||
a {
|
||||
|
||||
&:focus {
|
||||
|
@ -38,21 +40,20 @@
|
|||
font-weight : 100;
|
||||
}
|
||||
span.label.pull-right {
|
||||
position : relative;
|
||||
top : 24px;
|
||||
right : 14px;
|
||||
position : absolute;
|
||||
top : 28px;
|
||||
right : 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.backdrop #nav-region {
|
||||
background-color : #000000;
|
||||
.opacity(0.85);
|
||||
}
|
||||
.backdrop {
|
||||
#nav-region {
|
||||
background-color : #000000;
|
||||
.opacity(0.85);
|
||||
}
|
||||
|
||||
|
||||
#nav-region li a:hover, #in-sub-nav li a.active {
|
||||
background-color : #555555;
|
||||
text-decoration : none;
|
||||
}
|
||||
|
||||
#nav-region {
|
||||
|
@ -62,6 +63,19 @@
|
|||
.span12 {
|
||||
margin-left : 0px;
|
||||
}
|
||||
|
||||
li {
|
||||
a {
|
||||
&:hover {
|
||||
background-color : #555555;
|
||||
text-decoration : none;
|
||||
}
|
||||
|
||||
.label {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search {
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backbone',
|
||||
'Health/HealthModel',
|
||||
'Mixins/backbone.signalr.mixin'
|
||||
], function (Backbone, HealthModel) {
|
||||
var Collection = Backbone.Collection.extend({
|
||||
url : window.NzbDrone.ApiRoot + '/health',
|
||||
model: HealthModel
|
||||
});
|
||||
|
||||
var collection = new Collection().bindSignalR();
|
||||
collection.fetch();
|
||||
return collection;
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backbone'
|
||||
], function (Backbone) {
|
||||
return Backbone.Model.extend({
|
||||
|
||||
});
|
||||
});
|
|
@ -0,0 +1,39 @@
|
|||
'use strict';
|
||||
define(
|
||||
[
|
||||
'underscore',
|
||||
'marionette',
|
||||
'Health/HealthCollection'
|
||||
], function (_, Marionette, HealthCollection) {
|
||||
return Marionette.ItemView.extend({
|
||||
initialize: function () {
|
||||
this.listenTo(HealthCollection, 'sync', this._healthSync);
|
||||
HealthCollection.fetch();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.$el.empty();
|
||||
|
||||
if (HealthCollection.length === 0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
var count = HealthCollection.length;
|
||||
var label = 'label-warning';
|
||||
var errors = HealthCollection.some(function (model) {
|
||||
return model.get('type') === 'error';
|
||||
});
|
||||
|
||||
if (errors) {
|
||||
label = 'label-important';
|
||||
}
|
||||
|
||||
this.$el.html('<span class="label pull-right {0}">{1}</span>'.format(label, count));
|
||||
return this;
|
||||
},
|
||||
|
||||
_healthSync: function () {
|
||||
this.render();
|
||||
}
|
||||
});
|
||||
});
|
|
@ -47,6 +47,7 @@
|
|||
<i class="icon-laptop"></i>
|
||||
<br>
|
||||
System
|
||||
<span id="x-health"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
|
@ -3,10 +3,15 @@ define(
|
|||
[
|
||||
'marionette',
|
||||
'jquery',
|
||||
'Health/HealthView',
|
||||
'Navbar/Search'
|
||||
], function (Marionette, $) {
|
||||
return Marionette.ItemView.extend({
|
||||
template: 'Navbar/NavbarTemplate',
|
||||
], function (Marionette, $, HealthView) {
|
||||
return Marionette.Layout.extend({
|
||||
template: 'Navbar/NavbarLayoutTemplate',
|
||||
|
||||
regions: {
|
||||
health: '#x-health'
|
||||
},
|
||||
|
||||
ui: {
|
||||
search: '.x-series-search'
|
||||
|
@ -18,6 +23,7 @@ define(
|
|||
|
||||
onRender: function () {
|
||||
this.ui.search.bindSearch();
|
||||
this.health.show(new HealthView());
|
||||
},
|
||||
|
||||
onClick: function (event) {
|
||||
|
@ -30,9 +36,9 @@ define(
|
|||
var href = event.target.getAttribute('href');
|
||||
|
||||
//if couldn't find it look up'
|
||||
if (!href && target.parent('a') && target.parent('a')[0]) {
|
||||
if (!href && target.closest('a') && target.closest('a')[0]) {
|
||||
|
||||
var linkElement = target.parent('a')[0];
|
||||
var linkElement = target.closest('a')[0];
|
||||
|
||||
href = linkElement.getAttribute('href');
|
||||
this.setActive(linkElement);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
define([
|
||||
'vent',
|
||||
'marionette',
|
||||
'vent',
|
||||
'marionette',
|
||||
'backgrid',
|
||||
'System/Info/DiskSpace/DiskSpaceCollection',
|
||||
'Shared/LoadingView',
|
||||
|
@ -14,6 +14,7 @@ define([
|
|||
regions: {
|
||||
grid: '#x-grid'
|
||||
},
|
||||
|
||||
columns:
|
||||
[
|
||||
{
|
||||
|
@ -37,6 +38,7 @@ define([
|
|||
this.collection = new DiskSpaceCollection();
|
||||
this.listenTo(this.collection, 'sync', this._showTable);
|
||||
},
|
||||
|
||||
onRender : function() {
|
||||
this.grid.show(new LoadingView());
|
||||
},
|
||||
|
@ -44,6 +46,7 @@ define([
|
|||
onShow: function() {
|
||||
this.collection.fetch();
|
||||
},
|
||||
|
||||
_showTable: function() {
|
||||
this.grid.show(new Backgrid.Grid({
|
||||
row: Backgrid.Row,
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
'use strict';
|
||||
define(
|
||||
[
|
||||
'Cells/NzbDroneCell'
|
||||
], function (NzbDroneCell) {
|
||||
return NzbDroneCell.extend({
|
||||
|
||||
className: 'log-level-cell',
|
||||
|
||||
render: function () {
|
||||
|
||||
var level = this._getValue();
|
||||
this.$el.html('<i class="icon-nd-health-{0}" title="{1}"/>'.format(this._getValue().toLowerCase(), level));
|
||||
|
||||
return this;
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,55 @@
|
|||
'use strict';
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'backgrid',
|
||||
'Health/HealthCollection',
|
||||
'System/Info/Health/HealthCell',
|
||||
'System/Info/Health/HealthOkView'
|
||||
], function (Marionette, Backgrid, HealthCollection, HealthCell, HealthOkView) {
|
||||
return Marionette.Layout.extend({
|
||||
template: 'System/Info/Health/HealthLayoutTemplate',
|
||||
|
||||
regions: {
|
||||
grid: '#x-health-grid'
|
||||
},
|
||||
|
||||
columns:
|
||||
[
|
||||
{
|
||||
name: 'type',
|
||||
label: '',
|
||||
cell: HealthCell
|
||||
},
|
||||
{
|
||||
name: 'message',
|
||||
label: 'Message',
|
||||
cell: 'string'
|
||||
}
|
||||
],
|
||||
|
||||
initialize: function () {
|
||||
this.listenTo(HealthCollection, 'sync', this.render);
|
||||
HealthCollection.fetch();
|
||||
},
|
||||
|
||||
onRender : function() {
|
||||
if (HealthCollection.length === 0) {
|
||||
this.grid.show(new HealthOkView());
|
||||
}
|
||||
|
||||
else {
|
||||
this._showTable();
|
||||
}
|
||||
},
|
||||
|
||||
_showTable: function() {
|
||||
this.grid.show(new Backgrid.Grid({
|
||||
row: Backgrid.Row,
|
||||
columns: this.columns,
|
||||
collection: HealthCollection,
|
||||
className:'table table-hover'
|
||||
}));
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,6 @@
|
|||
<fieldset class="x-health">
|
||||
<legend>Health</legend>
|
||||
|
||||
<div id="x-health-grid"/>
|
||||
</fieldset>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
'use strict';
|
||||
define(
|
||||
[
|
||||
'marionette'
|
||||
], function (Marionette) {
|
||||
return Marionette.ItemView.extend({
|
||||
template: 'System/Info/Health/HealthOkViewTemplate'
|
||||
});
|
||||
});
|
|
@ -0,0 +1,2 @@
|
|||
No issues with your configuration
|
||||
|
|
@ -4,22 +4,26 @@ define(
|
|||
'backbone',
|
||||
'marionette',
|
||||
'System/Info/About/AboutView',
|
||||
'System/Info/DiskSpace/DiskSpaceLayout'
|
||||
'System/Info/DiskSpace/DiskSpaceLayout',
|
||||
'System/Info/Health/HealthLayout'
|
||||
], function (Backbone,
|
||||
Marionette,
|
||||
AboutView,
|
||||
DiskSpaceLayout) {
|
||||
DiskSpaceLayout,
|
||||
HealthLayout) {
|
||||
return Marionette.Layout.extend({
|
||||
template: 'System/Info/SystemInfoLayoutTemplate',
|
||||
|
||||
regions: {
|
||||
about : '#about',
|
||||
diskSpace: '#diskspace'
|
||||
diskSpace: '#diskspace',
|
||||
health : '#health'
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
this.about.show(new AboutView());
|
||||
this.diskSpace.show(new DiskSpaceLayout());
|
||||
this.health.show(new HealthLayout());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
<div class="row">
|
||||
<div class="span12" id="health"></div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="span12" id="about"></div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -33,9 +33,9 @@ define(
|
|||
|
||||
var href = event.target.getAttribute('href');
|
||||
|
||||
if (!href && $target.parent('a') && $target.parent('a')[0]) {
|
||||
if (!href && $target.closest('a') && $target.closest('a')[0]) {
|
||||
|
||||
var linkElement = $target.parent('a')[0];
|
||||
var linkElement = $target.closest('a')[0];
|
||||
|
||||
href = linkElement.getAttribute('href');
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue