diff --git a/src/NzbDrone.Api/Extensions/Pipelines/CacheHeaderPipeline.cs b/src/NzbDrone.Api/Extensions/Pipelines/CacheHeaderPipeline.cs index 1ac4ae86c..28cecd695 100644 --- a/src/NzbDrone.Api/Extensions/Pipelines/CacheHeaderPipeline.cs +++ b/src/NzbDrone.Api/Extensions/Pipelines/CacheHeaderPipeline.cs @@ -13,7 +13,10 @@ namespace NzbDrone.Api.Extensions.Pipelines private void Handle(NancyContext context) { - context.Response.Headers.Add("X-ApplicationVersion", BuildInfo.Version.ToString()); + if (!context.Response.Headers.ContainsKey("X-ApplicationVersion")) + { + context.Response.Headers.Add("X-ApplicationVersion", BuildInfo.Version.ToString()); + } } } } \ No newline at end of file diff --git a/src/NzbDrone.Api/Frontend/Mappers/LogFileMapper.cs b/src/NzbDrone.Api/Frontend/Mappers/LogFileMapper.cs index bc7124a97..588775839 100644 --- a/src/NzbDrone.Api/Frontend/Mappers/LogFileMapper.cs +++ b/src/NzbDrone.Api/Frontend/Mappers/LogFileMapper.cs @@ -6,11 +6,11 @@ using NzbDrone.Common.EnvironmentInfo; namespace NzbDrone.Api.Frontend.Mappers { - public class LogFileMapper : StaticResourceMapperBase + public class UpdateLogFileMapper : StaticResourceMapperBase { private readonly IAppFolderInfo _appFolderInfo; - public LogFileMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, Logger logger) + public UpdateLogFileMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, Logger logger) : base(diskProvider, logger) { _appFolderInfo = appFolderInfo; @@ -21,12 +21,12 @@ namespace NzbDrone.Api.Frontend.Mappers var path = resourceUrl.Replace('/', Path.DirectorySeparatorChar); path = Path.GetFileName(path); - return Path.Combine(_appFolderInfo.GetLogFolder(), path); + return Path.Combine(_appFolderInfo.GetUpdateLogFolder(), path); } public override bool CanHandle(string resourceUrl) { - return resourceUrl.StartsWith("/logfile/") && resourceUrl.EndsWith(".txt"); + return resourceUrl.StartsWith("/updatelogfile/") && resourceUrl.EndsWith(".txt"); } } } \ No newline at end of file diff --git a/src/NzbDrone.Api/Frontend/Mappers/UpdateLogFileMapper.cs b/src/NzbDrone.Api/Frontend/Mappers/UpdateLogFileMapper.cs new file mode 100644 index 000000000..bc7124a97 --- /dev/null +++ b/src/NzbDrone.Api/Frontend/Mappers/UpdateLogFileMapper.cs @@ -0,0 +1,32 @@ +using System.IO; +using NLog; +using NzbDrone.Common; +using NzbDrone.Common.Disk; +using NzbDrone.Common.EnvironmentInfo; + +namespace NzbDrone.Api.Frontend.Mappers +{ + public class LogFileMapper : StaticResourceMapperBase + { + private readonly IAppFolderInfo _appFolderInfo; + + public LogFileMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, Logger logger) + : base(diskProvider, logger) + { + _appFolderInfo = appFolderInfo; + } + + protected override string Map(string resourceUrl) + { + var path = resourceUrl.Replace('/', Path.DirectorySeparatorChar); + path = Path.GetFileName(path); + + return Path.Combine(_appFolderInfo.GetLogFolder(), path); + } + + public override bool CanHandle(string resourceUrl) + { + return resourceUrl.StartsWith("/logfile/") && resourceUrl.EndsWith(".txt"); + } + } +} \ No newline at end of file diff --git a/src/NzbDrone.Api/Logs/LogFileModule.cs b/src/NzbDrone.Api/Logs/LogFileModule.cs index b44e77b6d..aaa8797d3 100644 --- a/src/NzbDrone.Api/Logs/LogFileModule.cs +++ b/src/NzbDrone.Api/Logs/LogFileModule.cs @@ -1,63 +1,43 @@ using System.Collections.Generic; using System.IO; -using System.Linq; using NzbDrone.Common; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; -using Nancy; -using Nancy.Responses; +using NzbDrone.Core.Configuration; namespace NzbDrone.Api.Logs { - public class LogFileModule : NzbDroneRestModule + public class LogFileModule : LogFileModuleBase { - private const string LOGFILE_ROUTE = @"/(?nzbdrone(?:\.\d+)?\.txt)"; - private readonly IAppFolderInfo _appFolderInfo; private readonly IDiskProvider _diskProvider; public LogFileModule(IAppFolderInfo appFolderInfo, - IDiskProvider diskProvider) - : base("log/file") + IDiskProvider diskProvider, + IConfigFileProvider configFileProvider) + : base(diskProvider, configFileProvider, "") { _appFolderInfo = appFolderInfo; _diskProvider = diskProvider; - GetResourceAll = GetLogFiles; + } - Get[LOGFILE_ROUTE] = options => GetLogFile(options.filename); + protected override IEnumerable GetLogFiles() + { + return _diskProvider.GetFiles(_appFolderInfo.GetLogFolder(), SearchOption.TopDirectoryOnly); } - private List GetLogFiles() + protected override string GetLogFilePath(string filename) { - var result = new List(); - - var files = _diskProvider.GetFiles(_appFolderInfo.GetLogFolder(), SearchOption.TopDirectoryOnly); + return Path.Combine(_appFolderInfo.GetLogFolder(), filename); + } - for (int i = 0; i < files.Length; i++) + protected override string DownloadUrlRoot + { + get { - var file = files[i]; - - result.Add(new LogFileResource - { - Id = i + 1, - Filename = Path.GetFileName(file), - LastWriteTime = _diskProvider.FileGetLastWriteUtc(file) - }); + return "logfile"; } - - return result.OrderByDescending(l => l.LastWriteTime).ToList(); } - private Response GetLogFile(string filename) - { - var filePath = Path.Combine(_appFolderInfo.GetLogFolder(), filename); - - if (!_diskProvider.FileExists(filePath)) - return new NotFoundResponse(); - - var data = _diskProvider.ReadAllText(filePath); - - return new TextResponse(data); - } } } \ No newline at end of file diff --git a/src/NzbDrone.Api/Logs/LogFileModuleBase.cs b/src/NzbDrone.Api/Logs/LogFileModuleBase.cs new file mode 100644 index 000000000..1b68a7a82 --- /dev/null +++ b/src/NzbDrone.Api/Logs/LogFileModuleBase.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using NzbDrone.Common.Disk; +using Nancy; +using Nancy.Responses; +using NzbDrone.Core.Configuration; + +namespace NzbDrone.Api.Logs +{ + public abstract class LogFileModuleBase : NzbDroneRestModule + { + protected const string LOGFILE_ROUTE = @"/(?[-.a-zA-Z0-9]+?\.txt)"; + + private readonly IDiskProvider _diskProvider; + private readonly IConfigFileProvider _configFileProvider; + + public LogFileModuleBase(IDiskProvider diskProvider, + IConfigFileProvider configFileProvider, + String route) + : base("log/file" + route) + { + _diskProvider = diskProvider; + _configFileProvider = configFileProvider; + GetResourceAll = GetLogFilesResponse; + + Get[LOGFILE_ROUTE] = options => GetLogFileResponse(options.filename); + } + + private List GetLogFilesResponse() + { + var result = new List(); + + var files = GetLogFiles().ToList(); + + for (int i = 0; i < files.Count; i++) + { + var file = files[i]; + var filename = Path.GetFileName(file); + + result.Add(new LogFileResource + { + Id = i + 1, + Filename = filename, + LastWriteTime = _diskProvider.FileGetLastWriteUtc(file), + ContentsUrl = String.Format("/api{0}/{1}/{2}", _configFileProvider.UrlBase, Resource, filename), + DownloadUrl = String.Format("{0}/{1}/{2}", _configFileProvider.UrlBase, DownloadUrlRoot, filename) + }); + } + + return result.OrderByDescending(l => l.LastWriteTime).ToList(); + } + + private Response GetLogFileResponse(String filename) + { + var filePath = GetLogFilePath(filename); + + if (!_diskProvider.FileExists(filePath)) + return new NotFoundResponse(); + + var data = _diskProvider.ReadAllText(filePath); + + return new TextResponse(data); + } + + protected abstract IEnumerable GetLogFiles(); + protected abstract String GetLogFilePath(String filename); + + protected abstract String DownloadUrlRoot { get; } + } +} \ No newline at end of file diff --git a/src/NzbDrone.Api/Logs/LogFileResource.cs b/src/NzbDrone.Api/Logs/LogFileResource.cs index 9d2847e89..8109eeb07 100644 --- a/src/NzbDrone.Api/Logs/LogFileResource.cs +++ b/src/NzbDrone.Api/Logs/LogFileResource.cs @@ -7,5 +7,7 @@ namespace NzbDrone.Api.Logs { public String Filename { get; set; } public DateTime LastWriteTime { get; set; } + public String ContentsUrl { get; set; } + public String DownloadUrl { get; set; } } } diff --git a/src/NzbDrone.Api/Logs/UpdateLogFileModule.cs b/src/NzbDrone.Api/Logs/UpdateLogFileModule.cs new file mode 100644 index 000000000..3c91173ce --- /dev/null +++ b/src/NzbDrone.Api/Logs/UpdateLogFileModule.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using NzbDrone.Common; +using NzbDrone.Common.Disk; +using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Core.Configuration; + +namespace NzbDrone.Api.Logs +{ + public class UpdateLogFileModule : LogFileModuleBase + { + private readonly IAppFolderInfo _appFolderInfo; + private readonly IDiskProvider _diskProvider; + + public UpdateLogFileModule(IAppFolderInfo appFolderInfo, + IDiskProvider diskProvider, + IConfigFileProvider configFileProvider) + : base(diskProvider, configFileProvider, "/update") + { + _appFolderInfo = appFolderInfo; + _diskProvider = diskProvider; + } + + protected override IEnumerable GetLogFiles() + { + return _diskProvider.GetFiles(_appFolderInfo.GetUpdateLogFolder(), SearchOption.TopDirectoryOnly) + .Where(f => Regex.IsMatch(Path.GetFileName(f), LOGFILE_ROUTE.TrimStart('/'), RegexOptions.IgnoreCase)) + .ToList(); + } + + protected override String GetLogFilePath(String filename) + { + return Path.Combine(_appFolderInfo.GetUpdateLogFolder(), filename); + } + + protected override String DownloadUrlRoot + { + get + { + return "updatelogfile"; + } + } + } +} \ No newline at end of file diff --git a/src/NzbDrone.Api/NzbDrone.Api.csproj b/src/NzbDrone.Api/NzbDrone.Api.csproj index 0c23ee731..fa1038d38 100644 --- a/src/NzbDrone.Api/NzbDrone.Api.csproj +++ b/src/NzbDrone.Api/NzbDrone.Api.csproj @@ -129,6 +129,7 @@ + @@ -141,6 +142,8 @@ + + diff --git a/src/NzbDrone.Core/Instrumentation/Commands/DeleteUpdateLogFilesCommand.cs b/src/NzbDrone.Core/Instrumentation/Commands/DeleteUpdateLogFilesCommand.cs new file mode 100644 index 000000000..a55e91502 --- /dev/null +++ b/src/NzbDrone.Core/Instrumentation/Commands/DeleteUpdateLogFilesCommand.cs @@ -0,0 +1,15 @@ +using NzbDrone.Core.Messaging.Commands; + +namespace NzbDrone.Core.Instrumentation.Commands +{ + public class DeleteUpdateLogFilesCommand : Command + { + public override bool SendUpdatesToClient + { + get + { + return true; + } + } + } +} \ No newline at end of file diff --git a/src/NzbDrone.Core/Instrumentation/DeleteLogFilesService.cs b/src/NzbDrone.Core/Instrumentation/DeleteLogFilesService.cs index 25ae79cad..e2e31ca7b 100644 --- a/src/NzbDrone.Core/Instrumentation/DeleteLogFilesService.cs +++ b/src/NzbDrone.Core/Instrumentation/DeleteLogFilesService.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using NLog; using NzbDrone.Common; using NzbDrone.Common.Disk; @@ -12,7 +13,7 @@ namespace NzbDrone.Core.Instrumentation { } - public class DeleteLogFilesService : IDeleteLogFilesService, IExecute + public class DeleteLogFilesService : IDeleteLogFilesService, IExecute, IExecute { private readonly IDiskProvider _diskProvider; private readonly IAppFolderInfo _appFolderInfo; @@ -27,12 +28,14 @@ namespace NzbDrone.Core.Instrumentation public void Execute(DeleteLogFilesCommand message) { - var logFiles = _diskProvider.GetFiles(_appFolderInfo.GetLogFolder(), SearchOption.TopDirectoryOnly); + _logger.Debug("Deleting all files in: {0}", _appFolderInfo.GetLogFolder()); + _diskProvider.EmptyFolder(_appFolderInfo.GetLogFolder()); + } - foreach (var logFile in logFiles) - { - _diskProvider.DeleteFile(logFile); - } + public void Execute(DeleteUpdateLogFilesCommand message) + { + _logger.Debug("Deleting all files in: {0}", _appFolderInfo.GetUpdateLogFolder()); + _diskProvider.EmptyFolder(_appFolderInfo.GetUpdateLogFolder()); } } } diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 8b70b8ca6..97f8093a6 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -334,6 +334,7 @@ + diff --git a/src/UI/System/Logs/Files/ContentsModel.js b/src/UI/System/Logs/Files/ContentsModel.js index d109013dc..baf529561 100644 --- a/src/UI/System/Logs/Files/ContentsModel.js +++ b/src/UI/System/Logs/Files/ContentsModel.js @@ -1,12 +1,11 @@ 'use strict'; define( [ - 'backbone', - 'System/StatusModel' - ], function (Backbone, StatusModel) { + 'backbone' + ], function (Backbone) { return Backbone.Model.extend({ url: function () { - return StatusModel.get('urlBase') + '/api/log/file/' + this.get('filename'); + return this.get('contentsUrl'); }, parse: function (contents) { diff --git a/src/UI/System/Logs/Files/DownloadLogCell.js b/src/UI/System/Logs/Files/DownloadLogCell.js index 6ce544374..63dfb66f2 100644 --- a/src/UI/System/Logs/Files/DownloadLogCell.js +++ b/src/UI/System/Logs/Files/DownloadLogCell.js @@ -1,16 +1,15 @@ 'use strict'; define( [ - 'Cells/NzbDroneCell', - 'System/StatusModel' - ], function (NzbDroneCell, StatusModel) { + '../../../Cells/NzbDroneCell' + ], function (NzbDroneCell) { return NzbDroneCell.extend({ className: 'download-log-cell', render: function () { this.$el.empty(); - this.$el.html('Download'.format(StatusModel.get('urlBase'), this.cellValue)); + this.$el.html('Download'.format(this.cellValue)); return this; } diff --git a/src/UI/System/Logs/Files/FilenameCell.js b/src/UI/System/Logs/Files/FilenameCell.js index 3fefb67a5..feec6ed8b 100644 --- a/src/UI/System/Logs/Files/FilenameCell.js +++ b/src/UI/System/Logs/Files/FilenameCell.js @@ -1,7 +1,7 @@ 'use strict'; define( [ - 'Cells/NzbDroneCell' + '../../../Cells/NzbDroneCell' ], function (NzbDroneCell) { return NzbDroneCell.extend({ diff --git a/src/UI/System/Logs/Files/LogFileLayout.js b/src/UI/System/Logs/Files/LogFileLayout.js index 8cffc029e..75280515a 100644 --- a/src/UI/System/Logs/Files/LogFileLayout.js +++ b/src/UI/System/Logs/Files/LogFileLayout.js @@ -7,7 +7,6 @@ define( 'System/Logs/Files/FilenameCell', 'Cells/RelativeDateCell', 'System/Logs/Files/DownloadLogCell', - 'System/Logs/Files/LogFileCollection', 'System/Logs/Files/Row', 'System/Logs/Files/ContentsView', 'System/Logs/Files/ContentsModel', @@ -20,7 +19,6 @@ define( FilenameCell, RelativeDateCell, DownloadLogCell, - LogFileCollection, LogFileRow, ContentsView, ContentsModel, @@ -48,18 +46,19 @@ define( cell : RelativeDateCell }, { - name : 'filename', + name : 'downloadUrl', label : '', cell : DownloadLogCell, sortable: false } ], - initialize: function () { - this.collection = new LogFileCollection(); + initialize: function (options) { + this.collection = options.collection; + this.deleteFilesCommand = options.deleteFilesCommand; - vent.on(vent.Commands.ShowLogFile, this._fetchLogFileContents, this); - vent.on(vent.Events.CommandComplete, this._commandComplete, this); + this.listenTo(vent, vent.Commands.ShowLogFile, this._fetchLogFileContents); + this.listenTo(vent, vent.Events.CommandComplete, this._commandComplete); this.listenTo(this.collection, 'sync', this._collectionSynced); this.collection.fetch(); @@ -83,11 +82,10 @@ define( ownerContext : this, callback : this._refreshTable }, - { title : 'Delete Log Files', icon : 'icon-trash', - command : 'deleteLogFiles', + command : this.deleteFilesCommand, successMessage : 'Log files have been deleted', errorMessage : 'Failed to delete log files' } @@ -125,11 +123,7 @@ define( this.contents.show(new LoadingView()); var model = options.model; - var filename = model.get('filename'); - - var contentsModel = new ContentsModel({ - filename: filename - }); + var contentsModel = new ContentsModel(model.toJSON()); this.listenToOnce(contentsModel, 'sync', this._showDetails); @@ -151,7 +145,7 @@ define( }, _commandComplete: function (options) { - if (options.command.get('name') === 'deletelogfiles') { + if (options.command.get('name') === this.deleteFilesCommand.toLowerCase()) { this._refreshTable(); } } diff --git a/src/UI/System/Logs/Files/Row.js b/src/UI/System/Logs/Files/Row.js index 926869008..b506e214d 100644 --- a/src/UI/System/Logs/Files/Row.js +++ b/src/UI/System/Logs/Files/Row.js @@ -1,7 +1,7 @@ 'use strict'; define( [ - 'vent', + '../../../vent', 'backgrid' ], function (vent, Backgrid) { diff --git a/src/UI/System/Logs/LogsLayout.js b/src/UI/System/Logs/LogsLayout.js index 15511b431..a8c1cf0ac 100644 --- a/src/UI/System/Logs/LogsLayout.js +++ b/src/UI/System/Logs/LogsLayout.js @@ -3,24 +3,29 @@ define( [ 'marionette', 'System/Logs/Table/LogsTableLayout', - 'System/Logs/Files/LogFileLayout' - ], function (Marionette, LogsTableLayout, LogsFileLayout) { + 'System/Logs/Files/LogFileLayout', + 'System/Logs/Files/LogFileCollection', + 'System/Logs/Updates/LogFileCollection' + ], function (Marionette, LogsTableLayout, LogsFileLayout, LogFileCollection, UpdateLogFileCollection) { return Marionette.Layout.extend({ template: 'System/Logs/LogsLayoutTemplate', ui: { - tableTab: '.x-table-tab', - filesTab: '.x-files-tab' + tableTab : '.x-table-tab', + filesTab : '.x-files-tab', + updateFilesTab : '.x-update-files-tab' }, regions: { - table: '#table', - files: '#files' + table : '#table', + files : '#files', + updateFiles : '#update-files' }, events: { - 'click .x-table-tab': '_showTable', - 'click .x-files-tab': '_showFiles' + 'click .x-table-tab' : '_showTable', + 'click .x-files-tab' : '_showFiles', + 'click .x-update-files-tab' : '_showUpdateFiles' }, onShow: function () { @@ -42,7 +47,22 @@ define( } this.ui.filesTab.tab('show'); - this.files.show(new LogsFileLayout()); + this.files.show(new LogsFileLayout({ + collection: new LogFileCollection(), + deleteFilesCommand: 'deleteLogFiles' + })); + }, + + _showUpdateFiles: function (e) { + if (e) { + e.preventDefault(); + } + + this.ui.updateFilesTab.tab('show'); + this.updateFiles.show(new LogsFileLayout({ + collection: new UpdateLogFileCollection(), + deleteFilesCommand: 'deleteUpdateLogFiles' + })); } }); }); diff --git a/src/UI/System/Logs/LogsLayoutTemplate.html b/src/UI/System/Logs/LogsLayoutTemplate.html index aa250b933..a9832519a 100644 --- a/src/UI/System/Logs/LogsLayoutTemplate.html +++ b/src/UI/System/Logs/LogsLayoutTemplate.html @@ -1,15 +1,17 @@ 
-
+ -
+
+
\ No newline at end of file diff --git a/src/UI/System/Logs/Updates/LogFileCollection.js b/src/UI/System/Logs/Updates/LogFileCollection.js new file mode 100644 index 000000000..9a6d928f9 --- /dev/null +++ b/src/UI/System/Logs/Updates/LogFileCollection.js @@ -0,0 +1,17 @@ +'use strict'; + +define( + [ + 'backbone', + 'System/Logs/Updates/LogFileModel' + ], function (Backbone, LogFileModel) { + return Backbone.Collection.extend({ + url : window.NzbDrone.ApiRoot + '/log/file/update', + model: LogFileModel, + + state: { + sortKey: 'lastWriteTime', + order : 1 + } + }); + }); diff --git a/src/UI/System/Logs/Updates/LogFileModel.js b/src/UI/System/Logs/Updates/LogFileModel.js new file mode 100644 index 000000000..17c0fd6da --- /dev/null +++ b/src/UI/System/Logs/Updates/LogFileModel.js @@ -0,0 +1,8 @@ +'use strict'; +define( + [ + 'backbone' + ], function (Backbone) { + return Backbone.Model.extend({ + }); + });