New: Update Nancy to 2.0

pull/980/head
ta264 5 years ago
parent 425bd8964f
commit 17c9fc419c

@ -15,10 +15,10 @@ namespace Lidarr.Api.V1.AlbumStudio
{
_artistService = artistService;
_albumMonitoredService = albumMonitoredService;
Post["/"] = artist => UpdateAll();
Post("/", artist => UpdateAll());
}
private Response UpdateAll()
private object UpdateAll()
{
//Read from request
var request = Request.Body.FromJson<AlbumStudioResource>();
@ -41,7 +41,7 @@ namespace Lidarr.Api.V1.AlbumStudio
_albumMonitoredService.SetAlbumMonitoredStatus(artist, request.MonitoringOptions);
}
return "ok".AsResponse(HttpStatusCode.Accepted);
return ResponseWithCode("ok", HttpStatusCode.Accepted);
}
}
}

@ -17,13 +17,13 @@ namespace Lidarr.Api.V1.Albums
: base("/album/lookup")
{
_searchProxy = searchProxy;
Get["/"] = x => Search();
Get("/", x => Search());
}
private Response Search()
private object Search()
{
var searchResults = _searchProxy.SearchForNewAlbum((string)Request.Query.term, null);
return MapToResource(searchResults).ToList().AsResponse();
return MapToResource(searchResults).ToList();
}
private static IEnumerable<AlbumResource> MapToResource(IEnumerable<NzbDrone.Core.Music.Album> albums)

@ -36,7 +36,7 @@ namespace Lidarr.Api.V1.Albums
_releaseService = releaseService;
GetResourceAll = GetAlbums;
UpdateResource = UpdateAlbum;
Put["/monitor"] = x => SetAlbumsMonitored();
Put("/monitor", x => SetAlbumsMonitored());
}
private List<AlbumResource> GetAlbums()
@ -95,13 +95,13 @@ namespace Lidarr.Api.V1.Albums
BroadcastResourceChange(ModelAction.Updated, model.Id);
}
private Response SetAlbumsMonitored()
private object SetAlbumsMonitored()
{
var resource = Request.Body.FromJson<AlbumsMonitoredResource>();
_albumService.SetMonitored(resource.AlbumIds, resource.Monitored);
return MapToResource(_albumService.GetAlbums(resource.AlbumIds), false).AsResponse(HttpStatusCode.Accepted);
return ResponseWithCode(MapToResource(_albumService.GetAlbums(resource.AlbumIds), false), HttpStatusCode.Accepted);
}
public void Handle(AlbumGrabbedEvent message)

@ -19,11 +19,11 @@ namespace Lidarr.Api.V1.Artist
{
_artistService = artistService;
_commandQueueManager = commandQueueManager;
Put["/"] = artist => SaveAll();
Delete["/"] = artist => DeleteArtist();
Put("/", artist => SaveAll());
Delete("/", artist => DeleteArtist());
}
private Response SaveAll()
private object SaveAll()
{
var resource = Request.Body.FromJson<ArtistEditorResource>();
var artistToUpdate = _artistService.GetArtists(resource.ArtistIds);
@ -91,12 +91,12 @@ namespace Lidarr.Api.V1.Artist
});
}
return _artistService.UpdateArtists(artistToUpdate, !resource.MoveFiles)
return ResponseWithCode(_artistService.UpdateArtists(artistToUpdate, !resource.MoveFiles)
.ToResource()
.AsResponse(HttpStatusCode.Accepted);
, HttpStatusCode.Accepted);
}
private Response DeleteArtist()
private object DeleteArtist()
{
var resource = Request.Body.FromJson<ArtistEditorResource>();
@ -105,7 +105,7 @@ namespace Lidarr.Api.V1.Artist
_artistService.DeleteArtist(artistId, false);
}
return new object().AsResponse();
return new object();
}
}
}

@ -14,16 +14,16 @@ namespace Lidarr.Api.V1.Artist
: base("/artist/import")
{
_addArtistService = addArtistService;
Post["/"] = x => Import();
Post("/", x => Import());
}
private Response Import()
private object Import()
{
var resource = Request.Body.FromJson<List<ArtistResource>>();
var newArtists = resource.ToModel();
return _addArtistService.AddArtists(newArtists).ToResource().AsResponse();
return _addArtistService.AddArtists(newArtists).ToResource();
}
}
}

@ -16,13 +16,13 @@ namespace Lidarr.Api.V1.Artist
: base("/artist/lookup")
{
_searchProxy = searchProxy;
Get["/"] = x => Search();
Get("/", x => Search());
}
private Response Search()
private object Search()
{
var searchResults = _searchProxy.SearchForNewArtist((string)Request.Query.term);
return MapToResource(searchResults).ToList().AsResponse();
return MapToResource(searchResults).ToList();
}
private static IEnumerable<ArtistResource> MapToResource(IEnumerable<NzbDrone.Core.Music.Artist> artist)

@ -29,10 +29,10 @@ namespace Lidarr.Api.V1.Calendar
_artistService = artistService;
_tagService = tagService;
Get["/Lidarr.ics"] = options => GetCalendarFeed();
Get("/Lidarr.ics", options => GetCalendarFeed());
}
private Response GetCalendarFeed()
private object GetCalendarFeed()
{
var pastDays = 7;
var futureDays = 28;

@ -33,7 +33,7 @@ namespace Lidarr.Api.V1.Config
GetResourceById = GetNamingConfig;
UpdateResource = UpdateNamingConfig;
Get["/examples"] = x => GetExamples(this.Bind<NamingConfigResource>());
Get("/examples", x => GetExamples(this.Bind<NamingConfigResource>()));
SharedValidator.RuleFor(c => c.StandardTrackFormat).ValidTrackFormat();
@ -75,7 +75,7 @@ namespace Lidarr.Api.V1.Config
return GetNamingConfig();
}
private JsonResponse<NamingExampleResource> GetExamples(NamingConfigResource config)
private object GetExamples(NamingConfigResource config)
{
if (config.Id == 0)
{
@ -104,7 +104,7 @@ namespace Lidarr.Api.V1.Config
? null
: _filenameSampleService.GetAlbumFolderSample(nameSpec);
return sampleResource.AsResponse();
return sampleResource;
}
private void ValidateFormatResult(NamingConfig nameSpec)

@ -24,49 +24,49 @@ namespace Lidarr.Api.V1.FileSystem
_fileSystemLookupService = fileSystemLookupService;
_diskProvider = diskProvider;
_diskScanService = diskScanService;
Get["/"] = x => GetContents();
Get["/type"] = x => GetEntityType();
Get["/mediafiles"] = x => GetMediaFiles();
Get("/", x => GetContents());
Get("/type", x => GetEntityType());
Get("/mediafiles", x => GetMediaFiles());
}
private Response GetContents()
private object GetContents()
{
var pathQuery = Request.Query.path;
var includeFiles = Request.GetBooleanQueryParameter("includeFiles");
var allowFoldersWithoutTrailingSlashes = Request.GetBooleanQueryParameter("allowFoldersWithoutTrailingSlashes");
return _fileSystemLookupService.LookupContents((string)pathQuery.Value, includeFiles, allowFoldersWithoutTrailingSlashes).AsResponse();
return _fileSystemLookupService.LookupContents((string)pathQuery.Value, includeFiles, allowFoldersWithoutTrailingSlashes);
}
private Response GetEntityType()
private object GetEntityType()
{
var pathQuery = Request.Query.path;
var path = (string)pathQuery.Value;
if (_diskProvider.FileExists(path))
{
return new { type = "file" }.AsResponse();
return new { type = "file" };
}
//Return folder even if it doesn't exist on disk to avoid leaking anything from the UI about the underlying system
return new { type = "folder" }.AsResponse();
return new { type = "folder" };
}
private Response GetMediaFiles()
private object GetMediaFiles()
{
var pathQuery = Request.Query.path;
var path = (string)pathQuery.Value;
if (!_diskProvider.FolderExists(path))
{
return new string[0].AsResponse();
return new string[0];
}
return _diskScanService.GetAudioFiles(path).Select(f => new {
Path = f.FullName,
RelativePath = path.GetRelativePath(f.FullName),
Name = f.Name
}).AsResponse();
});
}
}
}

@ -30,9 +30,9 @@ namespace Lidarr.Api.V1.History
_failedDownloadService = failedDownloadService;
GetResourcePaged = GetHistory;
Get["/since"] = x => GetHistorySince();
Get["/artist"] = x => GetArtistHistory();
Post["/failed"] = x => MarkAsFailed();
Get("/since", x => GetHistorySince());
Get("/artist", x => GetArtistHistory());
Post("/failed", x => MarkAsFailed());
}
protected HistoryResource MapToResource(NzbDrone.Core.History.History model, bool includeArtist, bool includeAlbum, bool includeTrack)
@ -143,11 +143,11 @@ namespace Lidarr.Api.V1.History
return _historyService.GetByArtist(artistId, eventType).Select(h => MapToResource(h, includeArtist, includeAlbum, includeTrack)).ToList();
}
private Response MarkAsFailed()
private object MarkAsFailed()
{
var id = (int)Request.Form.Id;
_failedDownloadService.MarkAsFailed(id);
return new object().AsResponse();
return new object();
}
}
}

@ -43,7 +43,7 @@ namespace Lidarr.Api.V1.Indexers
_logger = logger;
GetResourceAll = GetReleases;
Post["/"] = x => DownloadRelease(ReadResourceFromRequest());
Post("/", x => DownloadRelease(ReadResourceFromRequest()));
PostValidator.RuleFor(s => s.IndexerId).ValidId();
PostValidator.RuleFor(s => s.Guid).NotEmpty();
@ -51,7 +51,7 @@ namespace Lidarr.Api.V1.Indexers
_remoteAlbumCache = cacheManager.GetCache<RemoteAlbum>(GetType(), "remoteAlbums");
}
private Response DownloadRelease(ReleaseResource release)
private object DownloadRelease(ReleaseResource release)
{
var remoteAlbum = _remoteAlbumCache.Find(GetCacheKey(release));
@ -72,7 +72,7 @@ namespace Lidarr.Api.V1.Indexers
throw new NzbDroneClientException(HttpStatusCode.Conflict, "Getting release from indexer failed");
}
return release.AsResponse();
return release;
}
private List<ReleaseResource> GetReleases()

@ -31,7 +31,7 @@ namespace Lidarr.Api.V1.Indexers
_indexerFactory = indexerFactory;
_logger = logger;
Post["/push"] = x => ProcessRelease(ReadResourceFromRequest());
Post("/push", x => ProcessRelease(ReadResourceFromRequest()));
PostValidator.RuleFor(s => s.Title).NotEmpty();
PostValidator.RuleFor(s => s.DownloadUrl).NotEmpty();
@ -39,7 +39,7 @@ namespace Lidarr.Api.V1.Indexers
PostValidator.RuleFor(s => s.PublishDate).NotEmpty();
}
private Response ProcessRelease(ReleaseResource release)
private object ProcessRelease(ReleaseResource release)
{
_logger.Info("Release pushed: {0} - {1}", release.Title, release.DownloadUrl);
@ -59,7 +59,7 @@ namespace Lidarr.Api.V1.Indexers
throw new ValidationException(new List<ValidationFailure> { new ValidationFailure("Title", "Unable to parse", release.Title) });
}
return MapDecisions(new[] { firstDecision }).First().AsResponse();
return MapDecisions(new[] { firstDecision }).First();
}
private void ResolveIndexer(ReleaseInfo release)

@ -13,9 +13,9 @@
<ItemGroup>
<PackageReference Include="FluentValidation" Version="6.2.1" />
<PackageReference Include="Ical.Net" Version="2.2.32" />
<PackageReference Include="Nancy" Version="1.4.4" />
<PackageReference Include="Nancy.Authentication.Basic" Version="1.4.1" />
<PackageReference Include="Nancy.Authentication.Forms" Version="1.4.1" />
<PackageReference Include="Nancy" Version="2.0.0" />
<PackageReference Include="Nancy.Authentication.Basic" Version="2.0.0" />
<PackageReference Include="Nancy.Authentication.Forms" Version="2.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="NLog" Version="4.5.4" />
<PackageReference Include="System.IO.Abstractions" Version="4.0.11" />

@ -1,8 +1,8 @@
using Nancy;
using Lidarr.Http;
namespace Lidarr.Api.V1
{
public abstract class LidarrV1FeedModule : NancyModule
public abstract class LidarrV1FeedModule : LidarrModule
{
protected LidarrV1FeedModule(string resource)
: base("/feed/v1/" + resource.Trim('/'))

@ -1,8 +1,8 @@
using Nancy;
using Lidarr.Http;
namespace Lidarr.Api.V1
{
public abstract class LidarrV1Module : NancyModule
public abstract class LidarrV1Module : LidarrModule
{
protected LidarrV1Module(string resource)
: base("/api/v1/" + resource.Trim('/'))

@ -26,7 +26,7 @@ namespace Lidarr.Api.V1.Logs
_configFileProvider = configFileProvider;
GetResourceAll = GetLogFilesResponse;
Get[LOGFILE_ROUTE] = options => GetLogFileResponse(options.filename);
Get(LOGFILE_ROUTE, options => GetLogFileResponse(options.filename));
}
private List<LogFileResource> GetLogFilesResponse()
@ -53,7 +53,7 @@ namespace Lidarr.Api.V1.Logs
return result.OrderByDescending(l => l.LastWriteTime).ToList();
}
private Response GetLogFileResponse(string filename)
private object GetLogFileResponse(string filename)
{
LogManager.Flush();

@ -33,11 +33,11 @@ namespace Lidarr.Api.V1.ManualImport
GetResourceAll = GetMediaFiles;
Put["/"] = options =>
Put("/", options =>
{
var resource = Request.Body.FromJson<List<ManualImportResource>>();
return UpdateImportItems(resource).AsResponse(HttpStatusCode.Accepted);
};
return ResponseWithCode(UpdateImportItems(resource), HttpStatusCode.Accepted);
});
}
private List<ManualImportResource> GetMediaFiles()

@ -23,11 +23,11 @@ namespace Lidarr.Api.V1.MediaCovers
_appFolderInfo = appFolderInfo;
_diskProvider = diskProvider;
Get[MEDIA_COVER_ARTIST_ROUTE] = options => GetArtistMediaCover(options.artistId, options.filename);
Get[MEDIA_COVER_ALBUM_ROUTE] = options => GetAlbumMediaCover(options.artistId, options.filename);
Get(MEDIA_COVER_ARTIST_ROUTE, options => GetArtistMediaCover(options.artistId, options.filename));
Get(MEDIA_COVER_ALBUM_ROUTE, options => GetAlbumMediaCover(options.artistId, options.filename));
}
private Response GetArtistMediaCover(int artistId, string filename)
private object GetArtistMediaCover(int artistId, string filename)
{
var filePath = Path.Combine(_appFolderInfo.GetAppDataPath(), "MediaCover", artistId.ToString(), filename);
@ -46,7 +46,7 @@ namespace Lidarr.Api.V1.MediaCovers
return new StreamResponse(() => File.OpenRead(filePath), MimeTypes.GetMimeType(filePath));
}
private Response GetAlbumMediaCover(int albumId, string filename)
private object GetAlbumMediaCover(int albumId, string filename)
{
var filePath = Path.Combine(_appFolderInfo.GetAppDataPath(), "MediaCover", "Albums", albumId.ToString(), filename);

@ -24,7 +24,7 @@ namespace Lidarr.Api.V1.Profiles.Delay
UpdateResource = Update;
CreateResource = Create;
DeleteResource = DeleteProfile;
Put[@"/reorder/(?<id>[\d]{1,10})"] = options => Reorder(options.Id);
Put(@"/reorder/(?<id>[\d]{1,10})", options => Reorder(options.Id));
SharedValidator.RuleFor(d => d.Tags).NotEmpty().When(d => d.Id != 1);
SharedValidator.RuleFor(d => d.Tags).EmptyCollection<DelayProfileResource, int>().When(d => d.Id == 1);
@ -77,14 +77,14 @@ namespace Lidarr.Api.V1.Profiles.Delay
return _delayProfileService.All().ToResource();
}
private Response Reorder(int id)
private object Reorder(int id)
{
ValidateId(id);
var afterIdQuery = Request.Query.After;
int? afterId = afterIdQuery.HasValue ? Convert.ToInt32(afterIdQuery.Value) : null;
return _delayProfileService.Reorder(id, afterId).ToResource().AsResponse();
return _delayProfileService.Reorder(id, afterId).ToResource();
}
}
}

@ -26,10 +26,10 @@ namespace Lidarr.Api.V1
_providerFactory = providerFactory;
_resourceMapper = resourceMapper;
Get["schema"] = x => GetTemplates();
Post["test"] = x => Test(ReadResourceFromRequest(true));
Post["testall"] = x => TestAll();
Post["action/{action}"] = x => RequestAction(x.action, ReadResourceFromRequest(true));
Get("schema", x => GetTemplates());
Post("test", x => Test(ReadResourceFromRequest(true)));
Post("testall", x => TestAll());
Post("action/{action}", x => RequestAction(x.action, ReadResourceFromRequest(true)));
GetResourceAll = GetAll;
GetResourceById = GetProviderById;
@ -112,7 +112,7 @@ namespace Lidarr.Api.V1
_providerFactory.Delete(id);
}
private Response GetTemplates()
private object GetTemplates()
{
var defaultDefinitions = _providerFactory.GetDefaultDefinitions().OrderBy(p => p.ImplementationName).ToList();
@ -133,10 +133,10 @@ namespace Lidarr.Api.V1
result.Add(providerResource);
}
return result.AsResponse();
return result;
}
private Response Test(TProviderResource providerResource)
private object Test(TProviderResource providerResource)
{
var providerDefinition = GetDefinition(providerResource, true);
@ -145,7 +145,7 @@ namespace Lidarr.Api.V1
return "{}";
}
private Response TestAll()
private object TestAll()
{
var providerDefinitions = _providerFactory.All()
.Where(c => c.Settings.Validate().IsValid && c.Enable)
@ -163,10 +163,10 @@ namespace Lidarr.Api.V1
});
}
return result.AsResponse(result.Any(c => !c.IsValid) ? HttpStatusCode.BadRequest : HttpStatusCode.OK);
return ResponseWithCode(result, result.Any(c => !c.IsValid) ? HttpStatusCode.BadRequest : HttpStatusCode.OK);
}
private Response RequestAction(string action, TProviderResource providerResource)
private object RequestAction(string action, TProviderResource providerResource)
{
var providerDefinition = GetDefinition(providerResource, true, false);

@ -18,7 +18,7 @@ namespace Lidarr.Api.V1.Qualities
GetResourceAll = GetAll;
GetResourceById = GetById;
UpdateResource = Update;
Put["/update"] = d => UpdateMany();
Put("/update", d => UpdateMany());
}
private void Update(QualityDefinitionResource resource)
@ -37,7 +37,7 @@ namespace Lidarr.Api.V1.Qualities
return _qualityDefinitionService.All().ToResource();
}
private Response UpdateMany()
private object UpdateMany()
{
//Read from request
var qualityDefinitions = Request.Body.FromJson<List<QualityDefinitionResource>>()
@ -46,9 +46,9 @@ namespace Lidarr.Api.V1.Qualities
_qualityDefinitionService.UpdateMany(qualityDefinitions);
return _qualityDefinitionService.All()
return ResponseWithCode(_qualityDefinitionService.All()
.ToResource()
.AsResponse(HttpStatusCode.Accepted);
, HttpStatusCode.Accepted);
}
}
}

@ -34,14 +34,14 @@ namespace Lidarr.Api.V1.Queue
_pendingReleaseService = pendingReleaseService;
_downloadService = downloadService;
Post[@"/grab/(?<id>[\d]{1,10})"] = x => Grab((int)x.Id);
Post["/grab/bulk"] = x => Grab();
Post(@"/grab/(?<id>[\d]{1,10})", x => Grab((int)x.Id));
Post("/grab/bulk", x => Grab());
Delete[@"/(?<id>[\d]{1,10})"] = x => Remove((int)x.Id);
Delete["/bulk"] = x => Remove();
Delete(@"/(?<id>[\d]{1,10})", x => Remove((int)x.Id));
Delete("/bulk", x => Remove());
}
private Response Grab(int id)
private object Grab(int id)
{
var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id);
@ -52,10 +52,10 @@ namespace Lidarr.Api.V1.Queue
_downloadService.DownloadReport(pendingRelease.RemoteAlbum);
return new object().AsResponse();
return new object();
}
private Response Grab()
private object Grab()
{
var resource = Request.Body.FromJson<QueueBulkResource>();
@ -71,10 +71,10 @@ namespace Lidarr.Api.V1.Queue
_downloadService.DownloadReport(pendingRelease.RemoteAlbum);
}
return new object().AsResponse();
return new object();
}
private Response Remove(int id)
private object Remove(int id)
{
var blacklist = Request.GetBooleanQueryParameter("blacklist");
var skipReDownload = Request.GetBooleanQueryParameter("skipredownload");
@ -86,10 +86,10 @@ namespace Lidarr.Api.V1.Queue
_trackedDownloadService.StopTracking(trackedDownload.DownloadItem.DownloadId);
}
return new object().AsResponse();
return new object();
}
private Response Remove()
private object Remove()
{
var blacklist = Request.GetBooleanQueryParameter("blacklist");
var skipReDownload = Request.GetBooleanQueryParameter("skipredownload");
@ -109,7 +109,7 @@ namespace Lidarr.Api.V1.Queue
_trackedDownloadService.StopTracking(trackedDownloadIds);
return new object().AsResponse();
return new object();
}
private TrackedDownload Remove(int id, bool blacklist, bool skipReDownload)

@ -1,6 +1,5 @@
using System;
using System.Linq;
using Nancy.Responses;
using NzbDrone.Common.TPL;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.Download.Pending;
@ -8,7 +7,6 @@ using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Queue;
using NzbDrone.SignalR;
using Lidarr.Http;
using Lidarr.Http.Extensions;
namespace Lidarr.Api.V1.Queue
{
@ -29,12 +27,12 @@ namespace Lidarr.Api.V1.Queue
_broadcastDebounce = new Debouncer(BroadcastChange, TimeSpan.FromSeconds(5));
Get["/"] = x => GetQueueStatusResponse();
Get("/", x => GetQueueStatusResponse());
}
private JsonResponse<QueueStatusResource> GetQueueStatusResponse()
private object GetQueueStatusResponse()
{
return GetQueueStatus().AsResponse();
return GetQueueStatus();
}
private QueueStatusResource GetQueueStatus()

@ -32,8 +32,8 @@ namespace Lidarr.Api.V1.System.Backup
GetResourceAll = GetBackupFiles;
DeleteResource = DeleteBackup;
Post[@"/restore/(?<id>[\d]{1,10})"] = x => Restore((int)x.Id);
Post["/restore/upload"] = x => UploadAndRestore();
Post(@"/restore/(?<id>[\d]{1,10})", x => Restore((int)x.Id));
Post("/restore/upload", x => UploadAndRestore());
}
public List<BackupResource> GetBackupFiles()
@ -65,7 +65,7 @@ namespace Lidarr.Api.V1.System.Backup
_diskProvider.DeleteFile(path);
}
public Response Restore(int id)
public object Restore(int id)
{
var backup = GetBackup(id);
@ -81,10 +81,10 @@ namespace Lidarr.Api.V1.System.Backup
return new
{
RestartRequired = true
}.AsResponse();
};
}
public Response UploadAndRestore()
public object UploadAndRestore()
{
var files = Context.Request.Files.ToList();
@ -112,7 +112,7 @@ namespace Lidarr.Api.V1.System.Backup
return new
{
RestartRequired = true
}.AsResponse();
};
}
private string GetBackupPath(NzbDrone.Core.Backup.Backup backup)

@ -39,13 +39,13 @@ namespace Lidarr.Api.V1.System
_configFileProvider = configFileProvider;
_database = database;
_lifecycleService = lifecycleService;
Get["/status"] = x => GetStatus();
Get["/routes"] = x => GetRoutes();
Post["/shutdown"] = x => Shutdown();
Post["/restart"] = x => Restart();
Get("/status", x => GetStatus());
Get("/routes", x => GetRoutes());
Post("/shutdown", x => Shutdown());
Post("/restart", x => Restart());
}
private Response GetStatus()
private object GetStatus()
{
return new
{
@ -74,24 +74,24 @@ namespace Lidarr.Api.V1.System
RuntimeVersion = _platformInfo.Version,
RuntimeName = PlatformInfo.Platform,
StartTime = _runtimeInfo.StartTime
}.AsResponse();
};
}
private Response GetRoutes()
private object GetRoutes()
{
return _routeCacheProvider.GetCache().Values.AsResponse();
return _routeCacheProvider.GetCache().Values;
}
private Response Shutdown()
private object Shutdown()
{
Task.Factory.StartNew(() => _lifecycleService.Shutdown());
return new { ShuttingDown = true }.AsResponse();
return new { ShuttingDown = true };
}
private Response Restart()
private object Restart()
{
Task.Factory.StartNew(() => _lifecycleService.Restart());
return new { Restarting = true }.AsResponse();
return new { Restarting = true };
}
}
}

@ -48,8 +48,8 @@ namespace Lidarr.Api.V1.TrackFiles
UpdateResource = SetQuality;
DeleteResource = DeleteTrackFile;
Put["/editor"] = trackFiles => SetQuality();
Delete["/bulk"] = trackFiles => DeleteTrackFiles();
Put("/editor", trackFiles => SetQuality());
Delete("/bulk", trackFiles => DeleteTrackFiles());
}
private TrackFileResource MapToResource(TrackFile trackFile)
@ -137,7 +137,7 @@ namespace Lidarr.Api.V1.TrackFiles
_mediaFileService.Update(trackFile);
}
private Response SetQuality()
private object SetQuality()
{
var resource = Request.Body.FromJson<TrackFileListResource>();
var trackFiles = _mediaFileService.Get(resource.TrackFileIds);
@ -152,8 +152,8 @@ namespace Lidarr.Api.V1.TrackFiles
_mediaFileService.Update(trackFiles);
return trackFiles.ConvertAll(f => f.ToResource(trackFiles.First().Artist.Value, _upgradableSpecification))
.AsResponse(Nancy.HttpStatusCode.Accepted);
return ResponseWithCode(trackFiles.ConvertAll(f => f.ToResource(trackFiles.First().Artist.Value, _upgradableSpecification))
, Nancy.HttpStatusCode.Accepted);
}
private void DeleteTrackFile(int id)
@ -175,7 +175,7 @@ namespace Lidarr.Api.V1.TrackFiles
}
}
private Response DeleteTrackFiles()
private object DeleteTrackFiles()
{
var resource = Request.Body.FromJson<TrackFileListResource>();
var trackFiles = _mediaFileService.Get(resource.TrackFileIds);
@ -186,7 +186,7 @@ namespace Lidarr.Api.V1.TrackFiles
_mediaFileDeletionService.DeleteTrackFile(artist, trackFile);
}
return new object().AsResponse();
return new object();
}
public void Handle(TrackFileAddedEvent message)

@ -1,62 +0,0 @@
using Nancy;
using Nancy.Authentication.Basic;
using Nancy.Authentication.Forms;
using Nancy.Bootstrapper;
using Nancy.Cryptography;
using NzbDrone.Api.Extensions.Pipelines;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Api.Authentication
{
public class EnableAuthInNancy : IRegisterNancyPipeline
{
private readonly IAuthenticationService _authenticationService;
private readonly IConfigService _configService;
private readonly IConfigFileProvider _configFileProvider;
public EnableAuthInNancy(IAuthenticationService authenticationService,
IConfigService configService,
IConfigFileProvider configFileProvider)
{
_authenticationService = authenticationService;
_configService = configService;
_configFileProvider = configFileProvider;
}
public void Register(IPipelines pipelines)
{
RegisterFormsAuth(pipelines);
pipelines.EnableBasicAuthentication(new BasicAuthenticationConfiguration(_authenticationService, "Sonarr"));
pipelines.BeforeRequest.AddItemToEndOfPipeline(RequiresAuthentication);
}
private Response RequiresAuthentication(NancyContext context)
{
Response response = null;
if (!_authenticationService.IsAuthenticated(context))
{
response = new Response { StatusCode = HttpStatusCode.Unauthorized };
}
return response;
}
private void RegisterFormsAuth(IPipelines pipelines)
{
var cryptographyConfiguration = new CryptographyConfiguration(
new RijndaelEncryptionProvider(new PassphraseKeyGenerator(_configService.RijndaelPassphrase,
new byte[] {1, 2, 3, 4, 5, 6, 7, 8})),
new DefaultHmacProvider(new PassphraseKeyGenerator(_configService.HmacPassphrase,
new byte[] {1, 2, 3, 4, 5, 6, 7, 8}))
);
FormsAuthentication.Enable(pipelines, new FormsAuthenticationConfiguration
{
RedirectUrl = "~/login",
UserMapper = _authenticationService,
CryptographyConfiguration = cryptographyConfiguration
});
}
}
}

@ -3,7 +3,6 @@ using Nancy;
using Nancy.Authentication.Forms;
using Nancy.Extensions;
using Nancy.ModelBinding;
using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.Extensions;
using NLog;
using NzbDrone.Common.Instrumentation;
@ -21,8 +20,8 @@ namespace Lidarr.Http.Authentication
{
_authService = authService;
_configFileProvider = configFileProvider;
Post["/login"] = x => Login(this.Bind<LoginResource>());
Get["/logout"] = x => Logout();
Post("/login", x => Login(this.Bind<LoginResource>()));
Get("/logout", x => Logout());
}
private Response Login(LoginResource resource)

@ -1,12 +1,12 @@
using System;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using Nancy;
using Nancy.Authentication.Basic;
using Nancy.Authentication.Forms;
using Nancy.Security;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Instrumentation;
using NzbDrone.Core.Authentication;
using NzbDrone.Core.Configuration;
using Lidarr.Http.Extensions;
@ -26,9 +26,8 @@ namespace Lidarr.Http.Authentication
public class AuthenticationService : IAuthenticationService
{
private static readonly Logger _authLogger = LogManager.GetLogger("Auth");
private static readonly NzbDroneUser AnonymousUser = new NzbDroneUser { UserName = "Anonymous" };
private const string AnonymousUser = "Anonymous";
private readonly IUserService _userService;
private readonly NancyContext _nancyContext;
private static string API_KEY;
private static AuthenticationType AUTH_METHOD;
@ -36,10 +35,9 @@ namespace Lidarr.Http.Authentication
[ThreadStatic]
private static NancyContext _context;
public AuthenticationService(IConfigFileProvider configFileProvider, IUserService userService, NancyContext nancyContext)
public AuthenticationService(IConfigFileProvider configFileProvider, IUserService userService)
{
_userService = userService;
_nancyContext = nancyContext;
API_KEY = configFileProvider.ApiKey;
AUTH_METHOD = configFileProvider.AuthenticationMethod;
}
@ -80,15 +78,15 @@ namespace Lidarr.Http.Authentication
if (context.CurrentUser != null)
{
LogLogout(context, context.CurrentUser.UserName);
LogLogout(context, context.CurrentUser.Identity.Name);
}
}
public IUserIdentity Validate(string username, string password)
public ClaimsPrincipal Validate(string username, string password)
{
if (AUTH_METHOD == AuthenticationType.None)
{
return AnonymousUser;
return new ClaimsPrincipal(new GenericIdentity(AnonymousUser));
}
var user = _userService.FindUser(username, password);
@ -101,7 +99,7 @@ namespace Lidarr.Http.Authentication
LogSuccess(_context, username);
}
return new NzbDroneUser { UserName = user.Username };
return new ClaimsPrincipal(new GenericIdentity(user.Username));
}
LogFailure(_context, username);
@ -109,18 +107,18 @@ namespace Lidarr.Http.Authentication
return null;
}
public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context)
public ClaimsPrincipal GetUserFromIdentifier(Guid identifier, NancyContext context)
{
if (AUTH_METHOD == AuthenticationType.None)
{
return AnonymousUser;
return new ClaimsPrincipal(new GenericIdentity(AnonymousUser));
}
var user = _userService.FindUser(identifier);
if (user != null)
{
return new NzbDroneUser { UserName = user.Username };
return new ClaimsPrincipal(new GenericIdentity(user.Username));
}
LogInvalidated(_context);

@ -77,7 +77,7 @@ namespace Lidarr.Http.Authentication
FormsAuthentication.FormsAuthenticationCookieName = "LidarrAuth";
var cryptographyConfiguration = new CryptographyConfiguration(
new RijndaelEncryptionProvider(new PassphraseKeyGenerator(_configService.RijndaelPassphrase, Encoding.ASCII.GetBytes(_configService.RijndaelSalt))),
new AesEncryptionProvider(new PassphraseKeyGenerator(_configService.RijndaelPassphrase, Encoding.ASCII.GetBytes(_configService.RijndaelSalt))),
new DefaultHmacProvider(new PassphraseKeyGenerator(_configService.HmacPassphrase, Encoding.ASCII.GetBytes(_configService.HmacSalt)))
);
@ -100,7 +100,7 @@ namespace Lidarr.Http.Authentication
context.Response.Headers["Location"].StartsWith($"{_configFileProvider.UrlBase}/login", StringComparison.InvariantCultureIgnoreCase)) ||
context.Response.StatusCode == HttpStatusCode.Unauthorized)
{
context.Response = new { Error = "Unauthorized" }.AsResponse(HttpStatusCode.Unauthorized);
context.Response = new { Error = "Unauthorized" }.AsResponse(context, HttpStatusCode.Unauthorized);
}
}
}

@ -1,12 +0,0 @@
using System.Collections.Generic;
using Nancy.Security;
namespace Lidarr.Http.Authentication
{
public class NzbDroneUser : IUserIdentity
{
public string UserName { get; set; }
public IEnumerable<string> Claims { get; set; }
}
}

@ -14,7 +14,9 @@ namespace Lidarr.Http.ErrorManagement
public void Handle(HttpStatusCode statusCode, NancyContext context)
{
if (statusCode == HttpStatusCode.SeeOther || statusCode == HttpStatusCode.OK)
{
return;
}
if (statusCode == HttpStatusCode.Continue)
{
@ -23,13 +25,17 @@ namespace Lidarr.Http.ErrorManagement
}
if (statusCode == HttpStatusCode.Unauthorized)
{
return;
}
if (context.Response.ContentType == "text/html" || context.Response.ContentType == "text/plain")
{
context.Response = new ErrorModel
{
Message = statusCode.ToString()
}.AsResponse(statusCode);
{
Message = statusCode.ToString()
}.AsResponse(context, statusCode);
}
}
}
}
}

@ -1,8 +1,9 @@
using System;
using System;
using System.Data.SQLite;
using FluentValidation;
using Nancy;
using NLog;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Exceptions;
using Lidarr.Http.Exceptions;
using Lidarr.Http.Extensions;
@ -28,7 +29,7 @@ namespace Lidarr.Http.ErrorManagement
if (apiException != null)
{
_logger.Warn(apiException, "API Error");
return apiException.ToErrorResponse();
return apiException.ToErrorResponse(context);
}
var validationException = exception as ValidationException;
@ -37,7 +38,7 @@ namespace Lidarr.Http.ErrorManagement
{
_logger.Warn("Invalid request {0}", validationException.Message);
return validationException.Errors.AsResponse(HttpStatusCode.BadRequest);
return validationException.Errors.AsResponse(context, HttpStatusCode.BadRequest);
}
var clientException = exception as NzbDroneClientException;
@ -48,7 +49,7 @@ namespace Lidarr.Http.ErrorManagement
{
Message = exception.Message,
Description = exception.ToString()
}.AsResponse((HttpStatusCode)clientException.StatusCode);
}.AsResponse(context, (HttpStatusCode)clientException.StatusCode);
}
var sqLiteException = exception as SQLiteException;
@ -61,7 +62,7 @@ namespace Lidarr.Http.ErrorManagement
return new ErrorModel
{
Message = exception.Message,
}.AsResponse(HttpStatusCode.Conflict);
}.AsResponse(context, HttpStatusCode.Conflict);
}
_logger.Error(sqLiteException, "[{0} {1}]", context.Request.Method, context.Request.Path);
@ -73,7 +74,7 @@ namespace Lidarr.Http.ErrorManagement
{
Message = exception.Message,
Description = exception.ToString()
}.AsResponse(HttpStatusCode.InternalServerError);
}.AsResponse(context, HttpStatusCode.InternalServerError);
}
}
}

@ -3,6 +3,7 @@ using Nancy;
using Nancy.Responses;
using Lidarr.Http.ErrorManagement;
using Lidarr.Http.Extensions;
using Nancy.Configuration;
namespace Lidarr.Http.Exceptions
{
@ -19,9 +20,9 @@ namespace Lidarr.Http.Exceptions
Content = content;
}
public JsonResponse<ErrorModel> ToErrorResponse()
public JsonResponse<ErrorModel> ToErrorResponse(NancyContext context)
{
return new ErrorModel(this).AsResponse(StatusCode);
return new ErrorModel(this).AsResponse(context, StatusCode);
}
private static string GetMessage(HttpStatusCode statusCode, object content)

@ -1,22 +1,23 @@
using System.Collections.Generic;
using System.IO;
using Nancy;
using Nancy.Responses.Negotiation;
using NzbDrone.Common.Serializer;
namespace Lidarr.Http.Extensions
{
public class NancyJsonSerializer : ISerializer
{
public bool CanSerialize(string contentType)
public bool CanSerialize(MediaRange contentType)
{
return true;
return contentType == "application/json";
}
public void Serialize<TModel>(string contentType, TModel model, Stream outputStream)
public void Serialize<TModel>(MediaRange contentType, TModel model, Stream outputStream)
{
Json.Serialize(model, outputStream);
}
public IEnumerable<string> Extensions { get; private set; }
}
}
}

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using Nancy;
using Nancy.Configuration;
using Nancy.Responses;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Serializer;
@ -32,9 +33,9 @@ namespace Lidarr.Http.Extensions
return Json.Deserialize(value, type);
}
public static JsonResponse<TModel> AsResponse<TModel>(this TModel model, HttpStatusCode statusCode = HttpStatusCode.OK)
public static JsonResponse<TModel> AsResponse<TModel>(this TModel model, NancyContext context, HttpStatusCode statusCode = HttpStatusCode.OK)
{
var response = new JsonResponse<TModel>(model, NancySerializer) { StatusCode = statusCode };
var response = new JsonResponse<TModel>(model, NancySerializer, context.Environment) { StatusCode = statusCode };
response.Headers.DisableCache();
return response;

@ -28,7 +28,7 @@ namespace Lidarr.Http.Frontend
_apiKey = configFileProvider.ApiKey;
_urlBase = configFileProvider.UrlBase;
Get["/initialize.js"] = x => Index();
Get("/initialize.js", x => Index());
}
private Response Index()

@ -18,8 +18,8 @@ namespace Lidarr.Http.Frontend
_requestMappers = requestMappers;
_logger = logger;
Get["/{resource*}"] = x => Index();
Get["/"] = x => Index();
Get("/{resource*}", x => Index());
Get("/", x => Index());
}
private Response Index()

@ -5,9 +5,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentValidation" Version="6.2.1" />
<PackageReference Include="Nancy" Version="1.4.4" />
<PackageReference Include="Nancy.Authentication.Basic" Version="1.4.1" />
<PackageReference Include="Nancy.Authentication.Forms" Version="1.4.1" />
<PackageReference Include="Nancy" Version="2.0.0" />
<PackageReference Include="Nancy.Authentication.Basic" Version="2.0.0" />
<PackageReference Include="Nancy.Authentication.Forms" Version="2.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="NLog" Version="4.5.4" />
</ItemGroup>

@ -51,7 +51,10 @@ namespace Lidarr.Http
return _tinyIoCContainer;
}
protected override DiagnosticsConfiguration DiagnosticsConfiguration => new DiagnosticsConfiguration { Password = @"password" };
public override void Configure(Nancy.Configuration.INancyEnvironment environment)
{
environment.Diagnostics(password: @"password");
}
protected override byte[] FavIcon => null;
}

@ -0,0 +1,18 @@
using Nancy;
using Nancy.Responses.Negotiation;
namespace Lidarr.Http
{
public abstract class LidarrModule : NancyModule
{
protected LidarrModule(string resource)
: base(resource)
{
}
protected Negotiator ResponseWithCode(object model, HttpStatusCode statusCode)
{
return Negotiate.WithModel(model).WithStatusCode(statusCode);
}
}
}

@ -6,6 +6,7 @@ using Nancy;
using NzbDrone.Core.Datastore;
using Lidarr.Http.Extensions;
using Newtonsoft.Json;
using Nancy.Responses.Negotiation;
namespace Lidarr.Http.REST
{
@ -72,13 +73,13 @@ namespace Lidarr.Http.REST
set
{
_deleteResource = value;
Delete[ID_ROUTE] = options =>
Delete(ID_ROUTE, options =>
{
ValidateId(options.Id);
DeleteResource((int)options.Id);
return new object().AsResponse();
};
return new object();
});
}
}
@ -88,7 +89,7 @@ namespace Lidarr.Http.REST
set
{
_getResourceById = value;
Get[ID_ROUTE] = options =>
Get(ID_ROUTE, options =>
{
ValidateId(options.Id);
try
@ -100,13 +101,13 @@ namespace Lidarr.Http.REST
return new NotFoundResponse();
}
return resource.AsResponse();
return resource;
}
catch (ModelNotFoundException)
{
return new NotFoundResponse();
}
};
});
}
}
@ -117,11 +118,11 @@ namespace Lidarr.Http.REST
{
_getResourceAll = value;
Get[ROOT_ROUTE] = options =>
Get(ROOT_ROUTE, options =>
{
var resource = GetResourceAll();
return resource.AsResponse();
};
return resource;
});
}
}
@ -132,11 +133,11 @@ namespace Lidarr.Http.REST
{
_getResourcePaged = value;
Get[ROOT_ROUTE] = options =>
Get(ROOT_ROUTE, options =>
{
var resource = GetResourcePaged(ReadPagingResourceFromRequest());
return resource.AsResponse();
};
return resource;
});
}
}
@ -147,11 +148,11 @@ namespace Lidarr.Http.REST
{
_getResourceSingle = value;
Get[ROOT_ROUTE] = options =>
Get(ROOT_ROUTE, options =>
{
var resource = GetResourceSingle();
return resource.AsResponse();
};
return resource;
});
}
}
@ -161,11 +162,11 @@ namespace Lidarr.Http.REST
set
{
_createResource = value;
Post[ROOT_ROUTE] = options =>
Post(ROOT_ROUTE, options =>
{
var id = CreateResource(ReadResourceFromRequest());
return GetResourceById(id).AsResponse(HttpStatusCode.Created);
};
return ResponseWithCode(GetResourceById(id), HttpStatusCode.Created);
});
}
}
@ -176,23 +177,28 @@ namespace Lidarr.Http.REST
set
{
_updateResource = value;
Put[ROOT_ROUTE] = options =>
Put(ROOT_ROUTE, options =>
{
var resource = ReadResourceFromRequest();
UpdateResource(resource);
return GetResourceById(resource.Id).AsResponse(HttpStatusCode.Accepted);
};
return ResponseWithCode(GetResourceById(resource.Id), HttpStatusCode.Accepted);
});
Put[ID_ROUTE] = options =>
Put(ID_ROUTE, options =>
{
var resource = ReadResourceFromRequest();
resource.Id = options.Id;
UpdateResource(resource);
return GetResourceById(resource.Id).AsResponse(HttpStatusCode.Accepted);
};
return ResponseWithCode(GetResourceById(resource.Id), HttpStatusCode.Accepted);
});
}
}
protected Negotiator ResponseWithCode(object model, HttpStatusCode statusCode)
{
return Negotiate.WithModel(model).WithStatusCode(statusCode);
}
protected TResource ReadResourceFromRequest(bool skipValidate = false)
{
var resource = new TResource();

@ -6,6 +6,7 @@ using System.Reflection;
using Nancy;
using Nancy.Diagnostics;
using Nancy.Bootstrapper;
using Nancy.Configuration;
namespace Lidarr.Http
{
@ -52,6 +53,47 @@ namespace Lidarr.Http
return this.ApplicationContainer.Resolve<INancyEngine>();
}
//
// Summary:
// Gets the Nancy.Configuration.INancyEnvironmentConfigurator used by th.
//
// Returns:
// An Nancy.Configuration.INancyEnvironmentConfigurator instance.
protected override INancyEnvironmentConfigurator GetEnvironmentConfigurator()
{
return this.ApplicationContainer.Resolve<INancyEnvironmentConfigurator>();
}
//
// Summary:
// Get the Nancy.Configuration.INancyEnvironment instance.
//
// Returns:
// An configured Nancy.Configuration.INancyEnvironment instance.
//
// Remarks:
// The boostrapper must be initialised (Nancy.Bootstrapper.INancyBootstrapper.Initialise)
// prior to calling this.
public override INancyEnvironment GetEnvironment()
{
return this.ApplicationContainer.Resolve<INancyEnvironment>();
}
//
// Summary:
// Registers an Nancy.Configuration.INancyEnvironment instance in the container.
//
// Parameters:
// container:
// The container to register into.
//
// environment:
// The Nancy.Configuration.INancyEnvironment instance to register.
protected override void RegisterNancyEnvironment(TinyIoCContainer container, INancyEnvironment environment)
{
ApplicationContainer.Register<INancyEnvironment>(environment);
}
/// <summary>
/// Create a default, unconfigured, container
/// </summary>

@ -5,7 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNet.SignalR.SelfHost" Version="2.4.1" />
<PackageReference Include="Nancy.Owin" Version="1.4.1" />
<PackageReference Include="Nancy.Owin" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Lidarr.Api.V1\Lidarr.Api.V1.csproj" />

Loading…
Cancel
Save