mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-20 21:33:15 -07:00
All Sln changes
This commit is contained in:
parent
b5855f2644
commit
796f0fc188
615 changed files with 68 additions and 747 deletions
114
Ombi.UI/Modules/Admin/AboutModule.cs
Normal file
114
Ombi.UI/Modules/Admin/AboutModule.cs
Normal file
|
@ -0,0 +1,114 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: AboutModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Nancy;
|
||||
using Nancy.Responses.Negotiation;
|
||||
using NLog;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Helpers.Permissions;
|
||||
using Ombi.UI.Models;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules.Admin
|
||||
{
|
||||
public class AboutModule : BaseModule
|
||||
{
|
||||
public AboutModule(ISettingsService<PlexRequestSettings> settingsService,
|
||||
ISettingsService<SystemSettings> systemService, ISecurityExtensions security,
|
||||
IStatusChecker statusChecker) : base("admin", settingsService, security)
|
||||
{
|
||||
Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
|
||||
|
||||
SettingsService = systemService;
|
||||
StatusChecker = statusChecker;
|
||||
|
||||
Get["/about", true] = async (x,ct) => await Index();
|
||||
Post["/about", true] = async (x,ct) => await ReportIssue();
|
||||
}
|
||||
|
||||
private ISettingsService<SystemSettings> SettingsService { get; }
|
||||
private IStatusChecker StatusChecker { get; }
|
||||
|
||||
|
||||
private async Task<Negotiator> Index()
|
||||
{
|
||||
var vm = new AboutAdminViewModel();
|
||||
|
||||
var systemSettings = await SettingsService.GetSettingsAsync();
|
||||
|
||||
var type = Type.GetType("Mono.Runtime");
|
||||
if (type != null) // mono
|
||||
{
|
||||
vm.Os = "Mono";
|
||||
var displayName = type.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
if (displayName != null)
|
||||
{
|
||||
|
||||
vm.SystemVersion = displayName.Invoke(null, null).ToString();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Windows
|
||||
vm.Os = OperatingSystemHelper.GetOs();
|
||||
vm.SystemVersion = Environment.Version.ToString();
|
||||
}
|
||||
|
||||
vm.ApplicationVersion = AssemblyHelper.GetFileVersion();
|
||||
vm.Branch = EnumHelper<Branches>.GetDisplayValue(systemSettings.Branch);
|
||||
vm.LogLevel = LogManager.Configuration.LoggingRules.FirstOrDefault(x => x.NameMatches("database"))?.Levels?.FirstOrDefault()?.Name ?? "Unknown";
|
||||
|
||||
return View["About", vm];
|
||||
}
|
||||
|
||||
private async Task<Response> ReportIssue()
|
||||
{
|
||||
var title = Request.Form["title"];
|
||||
var body = Request.Form["body"];
|
||||
|
||||
if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(body))
|
||||
{
|
||||
return
|
||||
Response.AsJson(
|
||||
new
|
||||
{
|
||||
result = false,
|
||||
message = "The title or issue body is empty! Please give me a bit more detail :)"
|
||||
});
|
||||
}
|
||||
|
||||
var result = await StatusChecker.ReportBug(title,body);
|
||||
return Response.AsJson(new {result = true, url = result.HtmlUrl.ToString()});
|
||||
}
|
||||
}
|
||||
}
|
1058
Ombi.UI/Modules/Admin/AdminModule.cs
Normal file
1058
Ombi.UI/Modules/Admin/AdminModule.cs
Normal file
File diff suppressed because it is too large
Load diff
72
Ombi.UI/Modules/Admin/CustomizationModule.cs
Normal file
72
Ombi.UI/Modules/Admin/CustomizationModule.cs
Normal file
|
@ -0,0 +1,72 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: CustomizationModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Nancy;
|
||||
using Nancy.ModelBinding;
|
||||
using Nancy.Responses.Negotiation;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers.Permissions;
|
||||
using Ombi.UI.Models;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules.Admin
|
||||
{
|
||||
public class CustomizationModule : BaseModule
|
||||
{
|
||||
public CustomizationModule(ISettingsService<PlexRequestSettings> settingsService, ISettingsService<CustomizationSettings> cust, ISecurityExtensions security) : base("admin", settingsService, security)
|
||||
{
|
||||
Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
|
||||
|
||||
Settings = cust;
|
||||
|
||||
Get["/customization", true] = async (x,ct) => await Index();
|
||||
Post["/customization", true] = async (x,ct) => await Save();
|
||||
}
|
||||
|
||||
private ISettingsService<CustomizationSettings> Settings { get; }
|
||||
|
||||
private async Task<Negotiator> Index()
|
||||
{
|
||||
var model = await Settings.GetSettingsAsync();
|
||||
|
||||
return View["customization", model];
|
||||
}
|
||||
|
||||
private async Task<Response> Save()
|
||||
{
|
||||
var model = this.Bind<CustomizationSettings>();
|
||||
|
||||
var result = await Settings.SaveSettingsAsync(model);
|
||||
|
||||
return Response.AsJson(result
|
||||
? new JsonResponseModel { Result = true }
|
||||
: new JsonResponseModel { Result = false, Message = "We could not save to the database, please try again" });
|
||||
}
|
||||
}
|
||||
}
|
73
Ombi.UI/Modules/Admin/FaultQueueModule.cs
Normal file
73
Ombi.UI/Modules/Admin/FaultQueueModule.cs
Normal file
|
@ -0,0 +1,73 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: SystemStatusModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System.Linq;
|
||||
using Nancy.Responses.Negotiation;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Helpers.Permissions;
|
||||
using Ombi.Store;
|
||||
using Ombi.Store.Models;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.UI.Models;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules.Admin
|
||||
{
|
||||
public class FaultQueueModule : BaseModule
|
||||
{
|
||||
public FaultQueueModule(ISettingsService<PlexRequestSettings> settingsService, IRepository<RequestQueue> requestQueue, ISecurityExtensions security) : base("admin", settingsService, security)
|
||||
{
|
||||
RequestQueue = requestQueue;
|
||||
|
||||
Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
|
||||
|
||||
Get["Index", "/faultqueue"] = x => Index();
|
||||
}
|
||||
|
||||
private IRepository<RequestQueue> RequestQueue { get; }
|
||||
|
||||
private Negotiator Index()
|
||||
{
|
||||
var requests = RequestQueue.GetAll();
|
||||
|
||||
var model = requests.Select(r => new FaultedRequestsViewModel
|
||||
{
|
||||
FaultType = (FaultTypeViewModel)(int)r.FaultType,
|
||||
Type = (RequestTypeViewModel)(int)r.Type,
|
||||
Title = ByteConverterHelper.ReturnObject<RequestedModel>(r.Content).Title,
|
||||
Id = r.Id,
|
||||
PrimaryIdentifier = r.PrimaryIdentifier,
|
||||
LastRetry = r.LastRetry,
|
||||
Message = r.Message
|
||||
}).ToList();
|
||||
|
||||
return View["RequestFaultQueue", model];
|
||||
}
|
||||
}
|
||||
}
|
149
Ombi.UI/Modules/Admin/SystemStatusModule.cs
Normal file
149
Ombi.UI/Modules/Admin/SystemStatusModule.cs
Normal file
|
@ -0,0 +1,149 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: SystemStatusModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using MarkdownSharp;
|
||||
using Nancy;
|
||||
using Nancy.ModelBinding;
|
||||
using Nancy.Responses.Negotiation;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Core.StatusChecker;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Helpers.Analytics;
|
||||
using Ombi.Helpers.Permissions;
|
||||
using Ombi.UI.Models;
|
||||
using Action = Ombi.Helpers.Analytics.Action;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules.Admin
|
||||
{
|
||||
public class SystemStatusModule : BaseModule
|
||||
{
|
||||
public SystemStatusModule(ISettingsService<PlexRequestSettings> settingsService, ICacheProvider cache, ISettingsService<SystemSettings> ss, ISecurityExtensions security, IAnalytics a) : base("admin", settingsService, security)
|
||||
{
|
||||
Cache = cache;
|
||||
SystemSettings = ss;
|
||||
Analytics = a;
|
||||
|
||||
Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
|
||||
|
||||
Get["/status", true] = async (x, ct) => await Status();
|
||||
Post["/save", true] = async (x, ct) => await Save();
|
||||
|
||||
Post["/autoupdate"] = x => AutoUpdate();
|
||||
}
|
||||
|
||||
private ICacheProvider Cache { get; }
|
||||
private ISettingsService<SystemSettings> SystemSettings { get; }
|
||||
private IAnalytics Analytics { get; }
|
||||
|
||||
private async Task<Negotiator> Status()
|
||||
{
|
||||
var settings = await SystemSettings.GetSettingsAsync();
|
||||
var checker = new StatusChecker(SystemSettings);
|
||||
var status = await Cache.GetOrSetAsync(CacheKeys.LastestProductVersion, async () => await checker.GetStatus(), 30);
|
||||
var md = new Markdown(new MarkdownOptions { AutoNewLines = true, AutoHyperlink = true });
|
||||
status.ReleaseNotes = md.Transform(status.ReleaseNotes);
|
||||
|
||||
settings.Status = status;
|
||||
|
||||
settings.BranchDropdown = new List<BranchDropdown>
|
||||
{
|
||||
new BranchDropdown
|
||||
{
|
||||
Name = EnumHelper<Branches>.GetDisplayValue(Branches.Stable),
|
||||
Value = Branches.Stable,
|
||||
Selected = settings.Branch == Branches.Stable
|
||||
},
|
||||
new BranchDropdown
|
||||
{
|
||||
Name = EnumHelper<Branches>.GetDisplayValue(Branches.EarlyAccessPreview),
|
||||
Value = Branches.EarlyAccessPreview,
|
||||
Selected = settings.Branch == Branches.EarlyAccessPreview
|
||||
},
|
||||
new BranchDropdown
|
||||
{
|
||||
Name = EnumHelper<Branches>.GetDisplayValue(Branches.Dev),
|
||||
Value = Branches.Dev,
|
||||
Selected = settings.Branch == Branches.Dev
|
||||
},
|
||||
};
|
||||
|
||||
return View["Status", settings];
|
||||
}
|
||||
|
||||
private async Task<Response> Save()
|
||||
{
|
||||
|
||||
var settings = this.Bind<SystemSettings>();
|
||||
|
||||
Analytics.TrackEventAsync(Category.Admin, Action.Update, $"Updated Branch {EnumHelper<Branches>.GetDisplayValue(settings.Branch)}", Username, CookieHelper.GetAnalyticClientId(Cookies));
|
||||
await SystemSettings.SaveSettingsAsync(settings);
|
||||
|
||||
// Clear the cache
|
||||
Cache.Remove(CacheKeys.LastestProductVersion);
|
||||
|
||||
return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully Saved your settings"});
|
||||
}
|
||||
|
||||
private Response AutoUpdate()
|
||||
{
|
||||
Analytics.TrackEventAsync(Category.Admin, Action.Update, "AutoUpdate", Username, CookieHelper.GetAnalyticClientId(Cookies));
|
||||
|
||||
var url = Request.Form["url"];
|
||||
var args = (string)Request.Form["args"].ToString();
|
||||
var lowered = args.ToLower();
|
||||
var appPath = Path.Combine(Path.GetDirectoryName(Assembly.GetAssembly(typeof(SystemStatusModule)).Location ?? string.Empty) ?? string.Empty, "PlexRequests.Updater.exe");
|
||||
|
||||
if (!string.IsNullOrEmpty(lowered))
|
||||
{
|
||||
if (lowered.Contains("plexrequests.exe"))
|
||||
{
|
||||
lowered = lowered.Replace("plexrequests.exe", "");
|
||||
}
|
||||
}
|
||||
|
||||
var startArgs = string.IsNullOrEmpty(lowered) ? appPath : $"{lowered} Plexrequests.Updater.exe";
|
||||
|
||||
var startInfo = Type.GetType("Mono.Runtime") != null
|
||||
? new ProcessStartInfo(startArgs) { Arguments = $"{url} {lowered}", }
|
||||
: new ProcessStartInfo(startArgs) { Arguments = $"{url} {lowered}" };
|
||||
|
||||
Process.Start(startInfo);
|
||||
|
||||
Environment.Exit(0);
|
||||
return Nancy.Response.NoBody;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
83
Ombi.UI/Modules/Admin/UserManagementSettingsModule.cs
Normal file
83
Ombi.UI/Modules/Admin/UserManagementSettingsModule.cs
Normal file
|
@ -0,0 +1,83 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: SystemStatusModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Nancy;
|
||||
using Nancy.ModelBinding;
|
||||
using Nancy.Responses.Negotiation;
|
||||
using Nancy.Validation;
|
||||
using NLog;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers.Permissions;
|
||||
using Ombi.UI.Helpers;
|
||||
using Ombi.UI.Models;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules.Admin
|
||||
{
|
||||
public class UserManagementSettingsModule : BaseModule
|
||||
{
|
||||
public UserManagementSettingsModule(ISettingsService<PlexRequestSettings> settingsService, ISettingsService<UserManagementSettings> umSettings, ISecurityExtensions security) : base("admin", settingsService, security)
|
||||
{
|
||||
UserManagementSettings = umSettings;
|
||||
|
||||
Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
|
||||
|
||||
Get["UserManagementSettings","/usermanagementsettings", true] = async(x,ct) => await Index();
|
||||
Post["/usermanagementsettings", true] = async(x,ct) => await Update();
|
||||
}
|
||||
|
||||
private ISettingsService<UserManagementSettings> UserManagementSettings { get; }
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private async Task<Negotiator> Index()
|
||||
{
|
||||
var model = await UserManagementSettings.GetSettingsAsync();
|
||||
|
||||
return View["UserManagementSettings", model];
|
||||
}
|
||||
|
||||
|
||||
private async Task<Response> Update()
|
||||
{
|
||||
var settings = this.Bind<UserManagementSettings>();
|
||||
var valid = this.Validate(settings);
|
||||
if (!valid.IsValid)
|
||||
{
|
||||
var error = valid.SendJsonError();
|
||||
Log.Info("Error validating User Management settings, message: {0}", error.Message);
|
||||
return Response.AsJson(error);
|
||||
}
|
||||
|
||||
var result = await UserManagementSettings.SaveSettingsAsync(settings);
|
||||
return Response.AsJson(result
|
||||
? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for User Management!" }
|
||||
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
|
||||
}
|
||||
}
|
||||
}
|
46
Ombi.UI/Modules/ApiDocsModule.cs
Normal file
46
Ombi.UI/Modules/ApiDocsModule.cs
Normal file
|
@ -0,0 +1,46 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: ApiDocsModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using Nancy.Responses.Negotiation;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class ApiDocsModule : BaseModule
|
||||
{
|
||||
public ApiDocsModule(ISettingsService<PlexRequestSettings> pr, ISecurityExtensions security) : base("apidocs", pr, security)
|
||||
{
|
||||
Get["/"] = x => Documentation();
|
||||
}
|
||||
public Negotiator Documentation()
|
||||
{
|
||||
return View["Index"];
|
||||
}
|
||||
}
|
||||
}
|
115
Ombi.UI/Modules/ApiRequestMetadataModule.cs
Normal file
115
Ombi.UI/Modules/ApiRequestMetadataModule.cs
Normal file
|
@ -0,0 +1,115 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: ApiRequestMetadataModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Nancy.Metadata.Modules;
|
||||
using Nancy.Swagger;
|
||||
using Ombi.Store;
|
||||
using Ombi.UI.Models;
|
||||
using Ombi.UI.Models.UserManagement;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class ApiRequestMetadataModule: MetadataModule<SwaggerRouteData>
|
||||
{
|
||||
public ApiRequestMetadataModule()
|
||||
{
|
||||
Describe["GetRequests"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/requests");
|
||||
with.Summary("The list of requests");
|
||||
|
||||
with.Notes("This returns a list of requests");
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
with.Model<ApiModel<List<RequestedModel>>>();
|
||||
});
|
||||
|
||||
Describe["GetRequest"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/requests/{id}");
|
||||
with.Summary("Get's a single request");
|
||||
|
||||
with.Notes("This returns a single request");
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
with.PathParam<int>("id");
|
||||
with.Model<ApiModel<List<RequestedModel>>>();
|
||||
});
|
||||
|
||||
Describe["PostRequests"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/requests");
|
||||
with.Summary("Create a new request");
|
||||
with.Model<ApiModel<bool>>();
|
||||
with.BodyParam<RequestedModel>("The request", true);
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
with.Notes("Creates a new request");
|
||||
});
|
||||
|
||||
Describe["PutRequests"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/requests");
|
||||
with.Summary("Updates an existing request");
|
||||
with.Model<ApiModel<bool>>();
|
||||
with.BodyParam<RequestedModel>("The request", true);
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
with.Notes("Updates an existing request e.g. Add a issue to the request");
|
||||
});
|
||||
|
||||
Describe["DeleteRequests"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/requests/{id}");
|
||||
with.Summary("Deletes an existing request");
|
||||
with.Model<ApiModel<bool>>();
|
||||
with.PathParam<int>("id", required:true);
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
with.Notes("Deletes an existing request. If the request doesn't exist we will return an error.");
|
||||
});
|
||||
|
||||
Describe["GetApiKey"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/apikey");
|
||||
with.Summary("Gets the Api Key for Plex Requests");
|
||||
with.Model<ApiModel<string>>();
|
||||
with.QueryParam<string>("username", required:true );
|
||||
with.QueryParam<string>("password", required: true );
|
||||
with.Notes("Get's the current api key for the application");
|
||||
});
|
||||
|
||||
Describe["PutCredentials"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/credentials/{username}");
|
||||
with.Summary("Sets a new password for the user");
|
||||
with.Model<ApiModel<string>>();
|
||||
with.PathParam<int>("username", required:true);
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
with.BodyParam<UserUpdateViewModel>("User update view model", true);
|
||||
with.Notes("Sets a new password for the user");
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
165
Ombi.UI/Modules/ApiRequestModule.cs
Normal file
165
Ombi.UI/Modules/ApiRequestModule.cs
Normal file
|
@ -0,0 +1,165 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: ApiRequestModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Nancy;
|
||||
using Nancy.Extensions;
|
||||
using Nancy.Validation;
|
||||
using Newtonsoft.Json;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Store;
|
||||
using Ombi.UI.Models;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class ApiRequestModule : BaseApiModule
|
||||
{
|
||||
public ApiRequestModule(IRequestService service, ISettingsService<PlexRequestSettings> pr, ISecurityExtensions security) : base("api", pr, security)
|
||||
{
|
||||
Get["GetRequests","/requests"] = x => GetRequests();
|
||||
Get["GetRequest","/requests/{id}"] = x => GetSingleRequests(x);
|
||||
Post["PostRequests", "/requests"] = x => CreateRequest();
|
||||
Put["PutRequests", "/requests"] = x => UpdateRequest();
|
||||
Delete["DeleteRequests", "/requests/{id}"] = x => DeleteRequest(x);
|
||||
|
||||
|
||||
RequestService = service;
|
||||
SettingsService = pr;
|
||||
}
|
||||
|
||||
private IRequestService RequestService { get; }
|
||||
private ISettingsService<PlexRequestSettings> SettingsService { get; }
|
||||
|
||||
public Response GetRequests()
|
||||
{
|
||||
var apiModel = new ApiModel<List<RequestedModel>> { Data = new List<RequestedModel>() };
|
||||
|
||||
var requests = RequestService.GetAll();
|
||||
apiModel.Data.AddRange(requests);
|
||||
|
||||
return ReturnReponse(apiModel);
|
||||
}
|
||||
|
||||
public Response GetSingleRequests(dynamic x)
|
||||
{
|
||||
var id = (int)x.id;
|
||||
var apiModel = new ApiModel<List<RequestedModel>> { Data = new List<RequestedModel>() };
|
||||
|
||||
var requests = RequestService.Get(id);
|
||||
if (string.IsNullOrEmpty(requests.Title))
|
||||
{
|
||||
apiModel.Error = true;
|
||||
apiModel.ErrorMessage = "Request does not exist";
|
||||
return ReturnReponse(apiModel);
|
||||
}
|
||||
apiModel.Data.Add(requests);
|
||||
|
||||
return ReturnReponse(apiModel);
|
||||
}
|
||||
|
||||
public Response CreateRequest()
|
||||
{
|
||||
var request = JsonConvert.DeserializeObject<RequestedModel>(Request.Body.AsString());
|
||||
var a = this.Validate(request);
|
||||
if (!a.IsValid)
|
||||
{
|
||||
return ReturnValidationReponse(a);
|
||||
}
|
||||
|
||||
var apiModel = new ApiModel<bool>();
|
||||
var result = RequestService.AddRequest(request);
|
||||
|
||||
if (result == -1)
|
||||
{
|
||||
apiModel.Error = true;
|
||||
apiModel.ErrorMessage = "Could not insert the new request into the database. Internal error.";
|
||||
return ReturnReponse(apiModel);
|
||||
}
|
||||
|
||||
apiModel.Data = true;
|
||||
|
||||
return ReturnReponse(apiModel);
|
||||
}
|
||||
|
||||
public Response UpdateRequest()
|
||||
{
|
||||
var request = JsonConvert.DeserializeObject<RequestedModel>(Request.Body.AsString());
|
||||
var a = this.Validate(request);
|
||||
if (!a.IsValid)
|
||||
{
|
||||
return ReturnValidationReponse(a);
|
||||
}
|
||||
|
||||
|
||||
var apiModel = new ApiModel<bool>();
|
||||
var result = RequestService.UpdateRequest(request);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
apiModel.Error = true;
|
||||
apiModel.ErrorMessage = "Could not update the request into the database. Internal error.";
|
||||
return ReturnReponse(apiModel);
|
||||
}
|
||||
|
||||
apiModel.Data = true;
|
||||
|
||||
return ReturnReponse(apiModel);
|
||||
}
|
||||
|
||||
public Response DeleteRequest(dynamic x)
|
||||
{
|
||||
var id = (int)x.id;
|
||||
var apiModel = new ApiModel<bool>();
|
||||
|
||||
try
|
||||
{
|
||||
var exisitingRequest = RequestService.Get(id);
|
||||
if (string.IsNullOrEmpty(exisitingRequest.Title))
|
||||
{
|
||||
apiModel.Error = true;
|
||||
apiModel.ErrorMessage = $"The request id {id} does not exist";
|
||||
return ReturnReponse(apiModel);
|
||||
}
|
||||
RequestService.DeleteRequest(exisitingRequest);
|
||||
apiModel.Data = true;
|
||||
|
||||
return ReturnReponse(apiModel);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
apiModel.Error = true;
|
||||
apiModel.ErrorMessage = "Could not delete the request from the database. Internal error.";
|
||||
return ReturnReponse(apiModel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
175
Ombi.UI/Modules/ApiSettingsMetadataModule.cs
Normal file
175
Ombi.UI/Modules/ApiSettingsMetadataModule.cs
Normal file
|
@ -0,0 +1,175 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: ApiSettingsMetadataModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using Nancy.Metadata.Modules;
|
||||
using Nancy.Swagger;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.UI.Models;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class ApiSettingsMetadataModule: MetadataModule<SwaggerRouteData>
|
||||
{
|
||||
public ApiSettingsMetadataModule()
|
||||
{
|
||||
Describe["GetAuthSettings"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/settings/authentication");
|
||||
with.Summary("Gets the authentication settings saved in the application");
|
||||
with.Model<ApiModel<AuthenticationSettings>>();
|
||||
with.Notes("Gets the authentication settings saved in the application");
|
||||
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
});
|
||||
|
||||
Describe["PostAuthSettings"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/settings/authentication");
|
||||
with.Summary("Saves the authentication settings saved in the application");
|
||||
with.Model<ApiModel<bool>>();
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
with.BodyParam<AuthenticationSettings>("Authentication settings", true);
|
||||
with.Notes("Saves the authentication settings saved in the application");
|
||||
});
|
||||
|
||||
Describe["GetPlexSettings"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/settings/plex");
|
||||
with.Summary("Gets the Plex settings saved in the application");
|
||||
with.Model<ApiModel<PlexSettings>>();
|
||||
with.Notes("Gets the Plex settings saved in the application");
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
});
|
||||
|
||||
Describe["PostPlexSettings"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/settings/plex");
|
||||
with.Summary("Saves the Plex settings saved in the application");
|
||||
with.Model<ApiModel<bool>>();
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
with.BodyParam<PlexSettings>("Plex settings", true);
|
||||
with.Notes("Saves the Plex settings saved in the application");
|
||||
});
|
||||
|
||||
|
||||
Describe["GetCouchPotatoSettings"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/settings/couchpotato");
|
||||
with.Summary("Gets the CouchPotato settings saved in the application");
|
||||
with.Model<ApiModel<CouchPotatoSettings>>();
|
||||
with.Notes("Gets the CouchPotato settings saved in the application");
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
});
|
||||
|
||||
Describe["PostCouchPotatoSettings"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/settings/couchpotato");
|
||||
with.Summary("Saves the CouchPotato settings saved in the application");
|
||||
with.Model<ApiModel<bool>>();
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
with.BodyParam<CouchPotatoSettings>("CouchPotato settings", true);
|
||||
with.Notes("Saves the CouchPotato settings saved in the application");
|
||||
});
|
||||
|
||||
Describe["GetSonarrSettings"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/settings/sonarr");
|
||||
with.Summary("Gets the sonarr settings saved in the application");
|
||||
with.Model<ApiModel<SonarrSettings>>();
|
||||
with.Notes("Gets the sonarr settings saved in the application");
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
});
|
||||
|
||||
Describe["PostSonarrSettings"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/settings/sonarr");
|
||||
with.Summary("Saves the sonarr settings saved in the application");
|
||||
with.Model<ApiModel<bool>>();
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
with.BodyParam<SonarrSettings>("sonarr settings", true);
|
||||
with.Notes("Saves the sonarr settings saved in the application");
|
||||
});
|
||||
|
||||
Describe["GetSickRageSettings"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/settings/sickrage");
|
||||
with.Summary("Gets the SickRage settings saved in the application");
|
||||
with.Model<ApiModel<SickRageSettings>>();
|
||||
with.Notes("Gets the SickRage settings saved in the application");
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
});
|
||||
|
||||
Describe["PostSickRageSettings"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/settings/sickrage");
|
||||
with.Summary("Saves the SickRage settings saved in the application");
|
||||
with.Model<ApiModel<bool>>();
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
with.BodyParam<SickRageSettings>("SickRage settings", true);
|
||||
with.Notes("Saves the sickrage settings saved in the application");
|
||||
});
|
||||
|
||||
Describe["GetHeadphonesSettings"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/settings/headphones");
|
||||
with.Summary("Gets the headphones settings saved in the application");
|
||||
with.Model<ApiModel<HeadphonesSettings>>();
|
||||
with.Notes("Gets the headphones settings saved in the application");
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
});
|
||||
|
||||
Describe["PostHeadphonesSettings"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/settings/sickrage");
|
||||
with.Summary("Saves the headphones settings saved in the application");
|
||||
with.Model<ApiModel<bool>>();
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
with.BodyParam<HeadphonesSettings>("headphones settings", true);
|
||||
with.Notes("Saves the headphones settings saved in the application");
|
||||
});
|
||||
|
||||
Describe["GetPlexRequestSettings"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/settings/plexrequest");
|
||||
with.Summary("Gets the plexrequest settings saved in the application");
|
||||
with.Model<ApiModel<PlexRequestSettings>>();
|
||||
with.Notes("Gets the plexrequest settings saved in the application");
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
});
|
||||
|
||||
Describe["PostPlexRequestSettings"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/settings/plexrequest");
|
||||
with.Summary("Saves the plexrequest settings saved in the application");
|
||||
with.Model<ApiModel<bool>>();
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
with.BodyParam<PlexRequestSettings>("plexrequest settings", true);
|
||||
with.Notes("Saves the plexrequest settings saved in the application");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
366
Ombi.UI/Modules/ApiSettingsModule.cs
Normal file
366
Ombi.UI/Modules/ApiSettingsModule.cs
Normal file
|
@ -0,0 +1,366 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: ApiModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using Nancy;
|
||||
using Nancy.Extensions;
|
||||
using Nancy.Validation;
|
||||
using Newtonsoft.Json;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.UI.Models;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class ApiSettingsModule : BaseApiModule
|
||||
{
|
||||
public ApiSettingsModule(ISettingsService<PlexRequestSettings> pr, ISettingsService<AuthenticationSettings> auth,
|
||||
ISettingsService<PlexSettings> plexSettings, ISettingsService<CouchPotatoSettings> cp,
|
||||
ISettingsService<SonarrSettings> sonarr, ISettingsService<SickRageSettings> sr, ISettingsService<HeadphonesSettings> hp, ISecurityExtensions security) : base("api", pr, security)
|
||||
{
|
||||
Get["GetVersion", "/version"] = x => GetVersion();
|
||||
|
||||
|
||||
Get["GetAuthSettings", "/settings/authentication"] = x => GetAuthSettings();
|
||||
Post["PostAuthSettings", "/settings/authentication"] = x => PostAuthSettings();
|
||||
|
||||
Get["GetPlexRequestSettings", "/settings/plexrequest"] = x => GetPrSettings();
|
||||
Post["PostPlexRequestSettings", "/settings/plexrequest"] = x => PostPrSettings();
|
||||
|
||||
Get["GetPlexSettings", "/settings/plex"] = x => GetPlexSettings();
|
||||
Post["PostPlexSettings", "/settings/plex"] = x => PostPlexSettings();
|
||||
|
||||
Get["GetCouchPotatoSettings", "/settings/couchpotato"] = x => GetCpSettings();
|
||||
Post["PostCouchPotatoSettings", "/settings/couchpotato"] = x => PostCpSettings();
|
||||
|
||||
Get["GetSonarrSettings", "/settings/sonarr"] = x => GetSonarrSettings();
|
||||
Post["PostSonarrSettings", "/settings/sonarr"] = x => PostSonarrSettings();
|
||||
|
||||
Get["GetSickRageSettings", "/settings/sickrage"] = x => GetSickRageSettings();
|
||||
Post["PostSickRageSettings", "/settings/sickrage"] = x => PostSickRageSettings();
|
||||
|
||||
Get["GetHeadphonesSettings", "/settings/headphones"] = x => GetHeadphonesSettings();
|
||||
Post["PostHeadphonesSettings", "/settings/headphones"] = x => PostHeadphonesSettings();
|
||||
|
||||
SettingsService = pr;
|
||||
AuthSettings = auth;
|
||||
PlexSettings = plexSettings;
|
||||
CpSettings = cp;
|
||||
SonarrSettings = sonarr;
|
||||
SickRageSettings = sr;
|
||||
HeadphonesSettings = hp;
|
||||
}
|
||||
|
||||
private ISettingsService<PlexRequestSettings> SettingsService { get; }
|
||||
private ISettingsService<AuthenticationSettings> AuthSettings { get; }
|
||||
private ISettingsService<PlexSettings> PlexSettings { get; }
|
||||
private ISettingsService<CouchPotatoSettings> CpSettings { get; }
|
||||
private ISettingsService<SonarrSettings> SonarrSettings { get; }
|
||||
private ISettingsService<SickRageSettings> SickRageSettings { get; }
|
||||
private ISettingsService<HeadphonesSettings> HeadphonesSettings { get; }
|
||||
|
||||
private Response GetVersion()
|
||||
{
|
||||
return ReturnReponse(AssemblyHelper.GetProductVersion());
|
||||
}
|
||||
|
||||
|
||||
private Response GetPrSettings()
|
||||
{
|
||||
var model = new ApiModel<PlexRequestSettings>();
|
||||
try
|
||||
{
|
||||
var settings = SettingsService.GetSettings();
|
||||
model.Data = settings;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
model.ErrorMessage = e.Message;
|
||||
model.Error = true;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
}
|
||||
|
||||
private Response PostPrSettings()
|
||||
{
|
||||
var newSettings = JsonConvert.DeserializeObject<PlexRequestSettings>(Request.Body.AsString());
|
||||
var result = this.Validate(newSettings);
|
||||
if (!result.IsValid)
|
||||
{
|
||||
return ReturnValidationReponse(result);
|
||||
}
|
||||
|
||||
var model = new ApiModel<bool>();
|
||||
var settings = SettingsService.SaveSettings(newSettings);
|
||||
if (settings)
|
||||
{
|
||||
model.Data = true;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
|
||||
model.Error = true;
|
||||
model.ErrorMessage = "Could not update the settings";
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
|
||||
private Response GetAuthSettings()
|
||||
{
|
||||
var model = new ApiModel<AuthenticationSettings>();
|
||||
try
|
||||
{
|
||||
var settings = AuthSettings.GetSettings();
|
||||
model.Data = settings;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
model.ErrorMessage = e.Message;
|
||||
model.Error = true;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
}
|
||||
|
||||
private Response PostAuthSettings()
|
||||
{
|
||||
var newSettings = JsonConvert.DeserializeObject<AuthenticationSettings>(Request.Body.AsString());
|
||||
var result = this.Validate(newSettings);
|
||||
if (!result.IsValid)
|
||||
{
|
||||
return ReturnValidationReponse(result);
|
||||
}
|
||||
|
||||
var model = new ApiModel<bool>();
|
||||
var settings = AuthSettings.SaveSettings(newSettings);
|
||||
if (settings)
|
||||
{
|
||||
model.Data = true;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
|
||||
model.Error = true;
|
||||
model.ErrorMessage = "Could not update the settings";
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
|
||||
private Response GetPlexSettings()
|
||||
{
|
||||
var model = new ApiModel<PlexSettings>();
|
||||
try
|
||||
{
|
||||
var settings = PlexSettings.GetSettings();
|
||||
model.Data = settings;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
model.ErrorMessage = e.Message;
|
||||
model.Error = true;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
}
|
||||
|
||||
private Response PostPlexSettings()
|
||||
{
|
||||
var newSettings = JsonConvert.DeserializeObject<PlexSettings>(Request.Body.AsString());
|
||||
var result = this.Validate(newSettings);
|
||||
if (!result.IsValid)
|
||||
{
|
||||
return ReturnValidationReponse(result);
|
||||
}
|
||||
|
||||
var model = new ApiModel<bool>();
|
||||
var settings = PlexSettings.SaveSettings(newSettings);
|
||||
if (settings)
|
||||
{
|
||||
model.Data = true;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
|
||||
model.Error = true;
|
||||
model.ErrorMessage = "Could not update the settings";
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
|
||||
private Response GetCpSettings()
|
||||
{
|
||||
var model = new ApiModel<CouchPotatoSettings>();
|
||||
try
|
||||
{
|
||||
var settings = CpSettings.GetSettings();
|
||||
model.Data = settings;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
model.ErrorMessage = e.Message;
|
||||
model.Error = true;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
}
|
||||
|
||||
private Response PostCpSettings()
|
||||
{
|
||||
var newSettings = JsonConvert.DeserializeObject<CouchPotatoSettings>(Request.Body.AsString());
|
||||
var result = this.Validate(newSettings);
|
||||
if (!result.IsValid)
|
||||
{
|
||||
return ReturnValidationReponse(result);
|
||||
}
|
||||
|
||||
var model = new ApiModel<bool>();
|
||||
var settings = CpSettings.SaveSettings(newSettings);
|
||||
if (settings)
|
||||
{
|
||||
model.Data = true;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
|
||||
model.Error = true;
|
||||
model.ErrorMessage = "Could not update the settings";
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
|
||||
private Response GetSonarrSettings()
|
||||
{
|
||||
var model = new ApiModel<SonarrSettings>();
|
||||
try
|
||||
{
|
||||
var settings = SonarrSettings.GetSettings();
|
||||
model.Data = settings;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
model.ErrorMessage = e.Message;
|
||||
model.Error = true;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
}
|
||||
|
||||
private Response PostSonarrSettings()
|
||||
{
|
||||
var newSettings = JsonConvert.DeserializeObject<SonarrSettings>(Request.Body.AsString());
|
||||
var result = this.Validate(newSettings);
|
||||
if (!result.IsValid)
|
||||
{
|
||||
return ReturnValidationReponse(result);
|
||||
}
|
||||
|
||||
var model = new ApiModel<bool>();
|
||||
var settings = SonarrSettings.SaveSettings(newSettings);
|
||||
if (settings)
|
||||
{
|
||||
model.Data = true;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
|
||||
model.Error = true;
|
||||
model.ErrorMessage = "Could not update the settings";
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
|
||||
private Response GetSickRageSettings()
|
||||
{
|
||||
var model = new ApiModel<SickRageSettings>();
|
||||
try
|
||||
{
|
||||
var settings = SickRageSettings.GetSettings();
|
||||
model.Data = settings;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
model.ErrorMessage = e.Message;
|
||||
model.Error = true;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
}
|
||||
|
||||
private Response PostSickRageSettings()
|
||||
{
|
||||
var newSettings = JsonConvert.DeserializeObject<SickRageSettings>(Request.Body.AsString());
|
||||
var result = this.Validate(newSettings);
|
||||
if (!result.IsValid)
|
||||
{
|
||||
return ReturnValidationReponse(result);
|
||||
}
|
||||
|
||||
var model = new ApiModel<bool>();
|
||||
var settings = SickRageSettings.SaveSettings(newSettings);
|
||||
if (settings)
|
||||
{
|
||||
model.Data = true;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
|
||||
model.Error = true;
|
||||
model.ErrorMessage = "Could not update the settings";
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
private Response GetHeadphonesSettings()
|
||||
{
|
||||
var model = new ApiModel<HeadphonesSettings>();
|
||||
try
|
||||
{
|
||||
var settings = HeadphonesSettings.GetSettings();
|
||||
model.Data = settings;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
model.ErrorMessage = e.Message;
|
||||
model.Error = true;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
}
|
||||
|
||||
private Response PostHeadphonesSettings()
|
||||
{
|
||||
var newSettings = JsonConvert.DeserializeObject<HeadphonesSettings>(Request.Body.AsString());
|
||||
var result = this.Validate(newSettings);
|
||||
if (!result.IsValid)
|
||||
{
|
||||
return ReturnValidationReponse(result);
|
||||
}
|
||||
|
||||
var model = new ApiModel<bool>();
|
||||
var settings = HeadphonesSettings.SaveSettings(newSettings);
|
||||
if (settings)
|
||||
{
|
||||
model.Data = true;
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
|
||||
model.Error = true;
|
||||
model.ErrorMessage = "Could not update the settings";
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
}
|
||||
}
|
62
Ombi.UI/Modules/ApiUserMetadataModule.cs
Normal file
62
Ombi.UI/Modules/ApiUserMetadataModule.cs
Normal file
|
@ -0,0 +1,62 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: ApiUserMetadataModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using Nancy.Metadata.Modules;
|
||||
using Nancy.Swagger;
|
||||
using Ombi.UI.Models;
|
||||
using Ombi.UI.Models.UserManagement;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class ApiUserMetadataModule: MetadataModule<SwaggerRouteData>
|
||||
{
|
||||
public ApiUserMetadataModule()
|
||||
{
|
||||
Describe["GetApiKey"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/apikey");
|
||||
with.Summary("Gets the Api Key for Plex Requests");
|
||||
with.Model<ApiModel<string>>();
|
||||
with.QueryParam<string>("username", required:true );
|
||||
with.QueryParam<string>("password", required: true );
|
||||
with.Notes("Get's the current api key for the application");
|
||||
});
|
||||
|
||||
Describe["PutCredentials"] = description => description.AsSwagger(with =>
|
||||
{
|
||||
with.ResourcePath("/credentials/{username}");
|
||||
with.Summary("Sets a new password for the user");
|
||||
with.Model<ApiModel<string>>();
|
||||
with.PathParam<int>("username", required:true);
|
||||
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
|
||||
with.BodyParam<UserUpdateViewModel>("User update view model", true);
|
||||
with.Notes("Sets a new password for the user");
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
102
Ombi.UI/Modules/ApiUserModule.cs
Normal file
102
Ombi.UI/Modules/ApiUserModule.cs
Normal file
|
@ -0,0 +1,102 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: ApiModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using Nancy;
|
||||
using Nancy.ModelBinding;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.UI.Models;
|
||||
using Ombi.UI.Models.UserManagement;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class ApiUserModule : BaseApiModule
|
||||
{
|
||||
public ApiUserModule(ISettingsService<PlexRequestSettings> pr, ICustomUserMapper m, ISecurityExtensions security) : base("api", pr, security)
|
||||
{
|
||||
|
||||
Put["PutCredentials", "/credentials/{username}"] = x => ChangePassword(x);
|
||||
|
||||
Get["GetApiKey", "/apikey"] = x => GetApiKey();
|
||||
|
||||
SettingsService = pr;
|
||||
UserMapper = m;
|
||||
}
|
||||
|
||||
private ISettingsService<PlexRequestSettings> SettingsService { get; }
|
||||
private ICustomUserMapper UserMapper { get; }
|
||||
|
||||
public Response ChangePassword(dynamic x)
|
||||
{
|
||||
var username = (string)x.username;
|
||||
var userModel = this.BindAndValidate<UserUpdateViewModel>();
|
||||
|
||||
if (!ModelValidationResult.IsValid)
|
||||
{
|
||||
return ReturnValidationReponse(ModelValidationResult);
|
||||
}
|
||||
|
||||
var valid = UserMapper.ValidateUser(username, userModel.CurrentPassword);
|
||||
if (valid == null)
|
||||
{
|
||||
var errorModel = new ApiModel<string> { Error = true, ErrorMessage = "Incorrect username or password" };
|
||||
return ReturnReponse(errorModel);
|
||||
}
|
||||
var result = UserMapper.UpdatePassword(username, userModel.CurrentPassword, userModel.NewPassword);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
var errorModel = new ApiModel<string> { Error = true, ErrorMessage = "Could not update the password. " };
|
||||
return ReturnReponse(errorModel);
|
||||
}
|
||||
|
||||
|
||||
var model = new ApiModel<string> { Data = "Successfully updated the password"};
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
public Response GetApiKey()
|
||||
{
|
||||
var user = Request.Query["username"];
|
||||
var password = Request.Query["password"];
|
||||
var result = UserMapper.ValidateUser(user, password);
|
||||
var model = new ApiModel<string>();
|
||||
if (result == null)
|
||||
{
|
||||
model.Error = true;
|
||||
model.ErrorMessage = "Incorrect username or password";
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
|
||||
var settings = SettingsService.GetSettings();
|
||||
model.Data = settings.ApiKey;
|
||||
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
288
Ombi.UI/Modules/ApplicationTesterModule.cs
Normal file
288
Ombi.UI/Modules/ApplicationTesterModule.cs
Normal file
|
@ -0,0 +1,288 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: ApplicationTesterModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Nancy;
|
||||
using Nancy.ModelBinding;
|
||||
using Nancy.Security;
|
||||
using Nancy.Validation;
|
||||
using NLog;
|
||||
using Ombi.Api.Interfaces;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.UI.Helpers;
|
||||
using Ombi.UI.Models;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class ApplicationTesterModule : BaseAuthModule
|
||||
{
|
||||
|
||||
public ApplicationTesterModule(ICouchPotatoApi cpApi, ISonarrApi sonarrApi, IPlexApi plexApi,
|
||||
ISickRageApi srApi, IHeadphonesApi hpApi, ISettingsService<PlexRequestSettings> pr, ISecurityExtensions security) : base("test", pr, security)
|
||||
{
|
||||
this.RequiresAuthentication();
|
||||
|
||||
CpApi = cpApi;
|
||||
SonarrApi = sonarrApi;
|
||||
PlexApi = plexApi;
|
||||
SickRageApi = srApi;
|
||||
HeadphonesApi = hpApi;
|
||||
|
||||
Post["/cp"] = _ => CouchPotatoTest();
|
||||
Post["/sonarr"] = _ => SonarrTest();
|
||||
Post["/plex"] = _ => PlexTest();
|
||||
Post["/sickrage"] = _ => SickRageTest();
|
||||
Post["/headphones"] = _ => HeadphonesTest();
|
||||
Post["/plexdb"] = _ => TestPlexDb();
|
||||
}
|
||||
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
private ISonarrApi SonarrApi { get; }
|
||||
private ICouchPotatoApi CpApi { get; }
|
||||
private IPlexApi PlexApi { get; }
|
||||
private ISickRageApi SickRageApi { get; }
|
||||
private IHeadphonesApi HeadphonesApi { get; }
|
||||
|
||||
private Response CouchPotatoTest()
|
||||
{
|
||||
var couchPotatoSettings = this.Bind<CouchPotatoSettings>();
|
||||
var valid = this.Validate(couchPotatoSettings);
|
||||
if (!valid.IsValid)
|
||||
{
|
||||
return Response.AsJson(valid.SendJsonError());
|
||||
}
|
||||
try
|
||||
{
|
||||
var status = CpApi.GetStatus(couchPotatoSettings.FullUri, couchPotatoSettings.ApiKey);
|
||||
return status.success
|
||||
? Response.AsJson(new JsonResponseModel { Result = true, Message = "Connected to CouchPotato successfully!" })
|
||||
: Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not connect to CouchPotato, please check your settings." });
|
||||
|
||||
}
|
||||
catch (Exception e) // Exceptions are expected if we cannot connect so we will just log and swallow them.
|
||||
{
|
||||
Log.Warn("Exception thrown when attempting to get CP's status: ");
|
||||
Log.Warn(e);
|
||||
var message = $"Could not connect to CouchPotato, please check your settings. <strong>Exception Message:</strong> {e.Message}";
|
||||
if (e.InnerException != null)
|
||||
{
|
||||
message = $"Could not connect to CouchPotato, please check your settings. <strong>Exception Message:</strong> {e.InnerException.Message}";
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = message });
|
||||
}
|
||||
}
|
||||
|
||||
private Response SonarrTest()
|
||||
{
|
||||
var sonarrSettings = this.Bind<SonarrSettings>();
|
||||
var valid = this.Validate(sonarrSettings);
|
||||
if (!valid.IsValid)
|
||||
{
|
||||
return Response.AsJson(valid.SendJsonError());
|
||||
}
|
||||
try
|
||||
{
|
||||
var status = SonarrApi.SystemStatus(sonarrSettings.ApiKey, sonarrSettings.FullUri);
|
||||
return status?.version != null
|
||||
? Response.AsJson(new JsonResponseModel { Result = true, Message = "Connected to Sonarr successfully!" })
|
||||
: Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not connect to Sonarr, please check your settings." });
|
||||
|
||||
}
|
||||
catch (Exception e) // Exceptions are expected, if we cannot connect so we will just log and swallow them.
|
||||
{
|
||||
Log.Warn("Exception thrown when attempting to get Sonarr's status: ");
|
||||
Log.Warn(e);
|
||||
var message = $"Could not connect to Sonarr, please check your settings. <strong>Exception Message:</strong> {e.Message}";
|
||||
if (e.InnerException != null)
|
||||
{
|
||||
message = $"Could not connect to Sonarr, please check your settings. <strong>Exception Message:</strong> {e.InnerException.Message}";
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = message });
|
||||
}
|
||||
}
|
||||
|
||||
private Response PlexTest()
|
||||
{
|
||||
var plexSettings = this.Bind<PlexSettings>();
|
||||
var valid = this.Validate(plexSettings);
|
||||
if (!valid.IsValid)
|
||||
{
|
||||
return Response.AsJson(valid.SendJsonError());
|
||||
}
|
||||
if (plexSettings?.PlexAuthToken == null)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Plex is not setup yet, you need to update your Authentication settings" });
|
||||
}
|
||||
try
|
||||
{
|
||||
var status = PlexApi.GetStatus(plexSettings.PlexAuthToken, plexSettings.FullUri);
|
||||
return status != null
|
||||
? Response.AsJson(new JsonResponseModel { Result = true, Message = "Connected to Plex successfully!" })
|
||||
: Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not connect to Plex, please check your settings." });
|
||||
|
||||
}
|
||||
catch (Exception e) // Exceptions are expected, if we cannot connect so we will just log and swallow them.
|
||||
{
|
||||
Log.Warn("Exception thrown when attempting to get Plex's status: ");
|
||||
Log.Warn(e);
|
||||
var message = $"Could not connect to Plex, please check your settings. <strong>Exception Message:</strong> {e.Message}";
|
||||
if (e.InnerException != null)
|
||||
{
|
||||
message = $"Could not connect to Plex, please check your settings. <strong>Exception Message:</strong> {e.InnerException.Message}";
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = message });
|
||||
}
|
||||
}
|
||||
|
||||
private Response SickRageTest()
|
||||
{
|
||||
var sickRageSettings = this.Bind<SickRageSettings>();
|
||||
var valid = this.Validate(sickRageSettings);
|
||||
if (!valid.IsValid)
|
||||
{
|
||||
return Response.AsJson(valid.SendJsonError());
|
||||
}
|
||||
try
|
||||
{
|
||||
var status = SickRageApi.Ping(sickRageSettings.ApiKey, sickRageSettings.FullUri);
|
||||
return status?.result == "success"
|
||||
? Response.AsJson(new JsonResponseModel { Result = true, Message = "Connected to SickRage successfully!" })
|
||||
: Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not connect to SickRage, please check your settings." });
|
||||
|
||||
}
|
||||
catch (Exception e) // Exceptions are expected, if we cannot connect so we will just log and swallow them.
|
||||
{
|
||||
Log.Warn("Exception thrown when attempting to get SickRage's status: ");
|
||||
Log.Warn(e);
|
||||
var message = $"Could not connect to SickRage, please check your settings. <strong>Exception Message:</strong> {e.Message}";
|
||||
if (e.InnerException != null)
|
||||
{
|
||||
message = $"Could not connect to SickRage, please check your settings. <strong>Exception Message:</strong> {e.InnerException.Message}";
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = message });
|
||||
}
|
||||
}
|
||||
|
||||
private Response HeadphonesTest()
|
||||
{
|
||||
var settings = this.Bind<HeadphonesSettings>();
|
||||
var valid = this.Validate(settings);
|
||||
if (!valid.IsValid)
|
||||
{
|
||||
return Response.AsJson(valid.SendJsonError());
|
||||
}
|
||||
try
|
||||
{
|
||||
var result = HeadphonesApi.GetVersion(settings.ApiKey, settings.FullUri);
|
||||
if (!string.IsNullOrEmpty(result.latest_version))
|
||||
{
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = true,
|
||||
Message = "Connected to Headphones successfully!"
|
||||
});
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not connect to Headphones, please check your settings." });
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Warn("Exception thrown when attempting to get Headphones's status: ");
|
||||
Log.Warn(e);
|
||||
var message = $"Could not connect to Headphones, please check your settings. <strong>Exception Message:</strong> {e.Message}";
|
||||
if (e.InnerException != null)
|
||||
{
|
||||
message = $"Could not connect to Headphones, please check your settings. <strong>Exception Message:</strong> {e.InnerException.Message}";
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = message }); ;
|
||||
}
|
||||
}
|
||||
|
||||
private Response TestPlexDb()
|
||||
{
|
||||
var settings = this.Bind<PlexSettings>();
|
||||
var valid = this.Validate(settings);
|
||||
if (!valid.IsValid)
|
||||
{
|
||||
return Response.AsJson(valid.SendJsonError());
|
||||
}
|
||||
try
|
||||
{
|
||||
var location = string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(settings.PlexDatabaseLocationOverride))
|
||||
{
|
||||
if (Type.GetType("Mono.Runtime") != null)
|
||||
{
|
||||
// Mono
|
||||
location = Path.Combine("/var/lib/plexmediaserver/Library/Application Support/",
|
||||
"Plex Media Server", "Plug-in Support", "Databases", "com.plexapp.plugins.library.db");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default Windows
|
||||
location = Path.Combine(Environment.ExpandEnvironmentVariables("%LOCALAPPDATA%"),
|
||||
"Plex Media Server", "Plug-in Support", "Databases", "com.plexapp.plugins.library.db");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
location = Path.Combine(settings.PlexDatabaseLocationOverride, "Plug-in Support", "Databases", "com.plexapp.plugins.library.db");
|
||||
}
|
||||
|
||||
if (File.Exists(location))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = true,
|
||||
Message = "Found the database!"
|
||||
});
|
||||
}
|
||||
|
||||
return Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = $"Could not find the database at the following full location : {location}"
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Warn("Exception thrown when attempting to find the plex database: ");
|
||||
Log.Warn(e);
|
||||
var message = $"Could not find Plex's DB, please check your settings. <strong>Exception Message:</strong> {e.Message}";
|
||||
if (e.InnerException != null)
|
||||
{
|
||||
message = $"Could not find Plex's DB, please check your settings. <strong>Exception Message:</strong> {e.InnerException.Message}";
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = message }); ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
524
Ombi.UI/Modules/ApprovalModule.cs
Normal file
524
Ombi.UI/Modules/ApprovalModule.cs
Normal file
|
@ -0,0 +1,524 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: ApprovalModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Nancy;
|
||||
using NLog;
|
||||
using Ombi.Api.Interfaces;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.Queue;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Helpers.Permissions;
|
||||
using Ombi.Store;
|
||||
using Ombi.UI.Helpers;
|
||||
using Ombi.UI.Models;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class ApprovalModule : BaseAuthModule
|
||||
{
|
||||
|
||||
public ApprovalModule(IRequestService service, ISettingsService<CouchPotatoSettings> cpService, ICouchPotatoApi cpApi, ISonarrApi sonarrApi,
|
||||
ISettingsService<SonarrSettings> sonarrSettings, ISickRageApi srApi, ISettingsService<SickRageSettings> srSettings,
|
||||
ISettingsService<HeadphonesSettings> hpSettings, IHeadphonesApi hpApi, ISettingsService<PlexRequestSettings> pr, ITransientFaultQueue faultQueue
|
||||
, ISecurityExtensions security) : base("approval", pr, security)
|
||||
{
|
||||
|
||||
Before += (ctx) => Security.AdminLoginRedirect(ctx, Permissions.Administrator,Permissions.ManageRequests);
|
||||
|
||||
Service = service;
|
||||
CpService = cpService;
|
||||
CpApi = cpApi;
|
||||
SonarrApi = sonarrApi;
|
||||
SonarrSettings = sonarrSettings;
|
||||
SickRageApi = srApi;
|
||||
SickRageSettings = srSettings;
|
||||
HeadphonesSettings = hpSettings;
|
||||
HeadphoneApi = hpApi;
|
||||
FaultQueue = faultQueue;
|
||||
|
||||
Post["/approve", true] = async (x, ct) => await Approve((int)Request.Form.requestid, (string)Request.Form.qualityId);
|
||||
Post["/deny", true] = async (x, ct) => await DenyRequest((int)Request.Form.requestid, (string)Request.Form.reason);
|
||||
Post["/approveall", true] = async (x, ct) => await ApproveAll();
|
||||
Post["/approveallmovies", true] = async (x, ct) => await ApproveAllMovies();
|
||||
Post["/approvealltvshows", true] = async (x, ct) => await ApproveAllTVShows();
|
||||
Post["/deleteallmovies", true] = async (x, ct) => await DeleteAllMovies();
|
||||
Post["/deletealltvshows", true] = async (x, ct) => await DeleteAllTVShows();
|
||||
Post["/deleteallalbums", true] = async (x, ct) => await DeleteAllAlbums();
|
||||
}
|
||||
|
||||
private IRequestService Service { get; }
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
private ISettingsService<SonarrSettings> SonarrSettings { get; }
|
||||
private ISettingsService<SickRageSettings> SickRageSettings { get; }
|
||||
private ISettingsService<CouchPotatoSettings> CpService { get; }
|
||||
private ISettingsService<HeadphonesSettings> HeadphonesSettings { get; }
|
||||
private ISonarrApi SonarrApi { get; }
|
||||
private ISickRageApi SickRageApi { get; }
|
||||
private ICouchPotatoApi CpApi { get; }
|
||||
private IHeadphonesApi HeadphoneApi { get; }
|
||||
private ITransientFaultQueue FaultQueue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Approves the specified request identifier.
|
||||
/// </summary>
|
||||
/// <param name="requestId">The request identifier.</param>
|
||||
/// <returns></returns>
|
||||
private async Task<Response> Approve(int requestId, string qualityId)
|
||||
{
|
||||
Log.Info("approving request {0}", requestId);
|
||||
|
||||
// Get the request from the DB
|
||||
var request = await Service.GetAsync(requestId);
|
||||
|
||||
if (request == null)
|
||||
{
|
||||
Log.Warn("Tried approving a request, but the request did not exist in the database, requestId = {0}", requestId);
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "There are no requests to approve. Please refresh." });
|
||||
}
|
||||
|
||||
switch (request.Type)
|
||||
{
|
||||
case RequestType.Movie:
|
||||
return await RequestMovieAndUpdateStatus(request, qualityId);
|
||||
case RequestType.TvShow:
|
||||
return await RequestTvAndUpdateStatus(request, qualityId);
|
||||
case RequestType.Album:
|
||||
return await RequestAlbumAndUpdateStatus(request);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(request));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Response> RequestTvAndUpdateStatus(RequestedModel request, string qualityId)
|
||||
{
|
||||
var sender = new TvSenderOld(SonarrApi, SickRageApi); // TODO put back
|
||||
|
||||
var sonarrSettings = await SonarrSettings.GetSettingsAsync();
|
||||
if (sonarrSettings.Enabled)
|
||||
{
|
||||
Log.Trace("Sending to Sonarr");
|
||||
var result = await sender.SendToSonarr(sonarrSettings, request, qualityId);
|
||||
Log.Trace("Sonarr Result: ");
|
||||
Log.Trace(result.DumpJson());
|
||||
if (!string.IsNullOrEmpty(result.title))
|
||||
{
|
||||
Log.Info("Sent successfully, Approving request now.");
|
||||
request.Approved = true;
|
||||
var requestResult = await Service.UpdateRequestAsync(request);
|
||||
Log.Trace("Approval result: {0}", requestResult);
|
||||
if (requestResult)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = true });
|
||||
}
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = "Updated Sonarr but could not approve it in PlexRequests :("
|
||||
});
|
||||
}
|
||||
return Response.AsJson(ValidationHelper.SendSonarrError(result.ErrorMessages));
|
||||
|
||||
}
|
||||
|
||||
var srSettings = await SickRageSettings.GetSettingsAsync();
|
||||
if (srSettings.Enabled)
|
||||
{
|
||||
Log.Trace("Sending to SickRage");
|
||||
var result = sender.SendToSickRage(srSettings, request, qualityId);
|
||||
Log.Trace("SickRage Result: ");
|
||||
Log.Trace(result.DumpJson());
|
||||
if (result?.result == "success")
|
||||
{
|
||||
Log.Info("Sent successfully, Approving request now.");
|
||||
request.Approved = true;
|
||||
var requestResult = await Service.UpdateRequestAsync(request);
|
||||
Log.Trace("Approval result: {0}", requestResult);
|
||||
return Response.AsJson(requestResult
|
||||
? new JsonResponseModel { Result = true }
|
||||
: new JsonResponseModel { Result = false, Message = "Updated SickRage but could not approve it in PlexRequests :(" });
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = result?.message != null ? "<b>Message From SickRage: </b>" + result.message : "Could not add the series to SickRage"
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
request.Approved = true;
|
||||
var res = await Service.UpdateRequestAsync(request);
|
||||
return Response.AsJson(res
|
||||
? new JsonResponseModel { Result = true, Message = "This has been approved, but It has not been sent to Sonarr/SickRage because it has not been configured" }
|
||||
: new JsonResponseModel { Result = false, Message = "Updated SickRage but could not approve it in PlexRequests :(" });
|
||||
}
|
||||
|
||||
private async Task<Response> RequestMovieAndUpdateStatus(RequestedModel request, string qualityId)
|
||||
{
|
||||
var cpSettings = await CpService.GetSettingsAsync();
|
||||
|
||||
Log.Info("Adding movie to CouchPotato : {0}", request.Title);
|
||||
if (!cpSettings.Enabled)
|
||||
{
|
||||
// Approve it
|
||||
request.Approved = true;
|
||||
Log.Warn("We approved movie: {0} but could not add it to CouchPotato because it has not been setup", request.Title);
|
||||
|
||||
// Update the record
|
||||
var inserted = await Service.UpdateRequestAsync(request);
|
||||
return Response.AsJson(inserted
|
||||
? new JsonResponseModel { Result = true, Message = "This has been approved, but It has not been sent to CouchPotato because it has not been configured." }
|
||||
: new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = "We could not approve this request. Please try again or check the logs."
|
||||
});
|
||||
}
|
||||
|
||||
var result = CpApi.AddMovie(request.ImdbId, cpSettings.ApiKey, request.Title, cpSettings.FullUri, string.IsNullOrEmpty(qualityId) ? cpSettings.ProfileId : qualityId);
|
||||
Log.Trace("Adding movie to CP result {0}", result);
|
||||
if (result)
|
||||
{
|
||||
// Approve it
|
||||
request.Approved = true;
|
||||
|
||||
// Update the record
|
||||
var inserted = await Service.UpdateRequestAsync(request);
|
||||
|
||||
return Response.AsJson(inserted
|
||||
? new JsonResponseModel { Result = true }
|
||||
: new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = "We could not approve this request. Please try again or check the logs."
|
||||
});
|
||||
}
|
||||
return
|
||||
Response.AsJson(
|
||||
new
|
||||
{
|
||||
Result = false,
|
||||
Message =
|
||||
"Something went wrong adding the movie to CouchPotato! Please check your settings."
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<Response> RequestAlbumAndUpdateStatus(RequestedModel request)
|
||||
{
|
||||
var hpSettings = await HeadphonesSettings.GetSettingsAsync();
|
||||
Log.Info("Adding album to Headphones : {0}", request.Title);
|
||||
if (!hpSettings.Enabled)
|
||||
{
|
||||
// Approve it
|
||||
request.Approved = true;
|
||||
Log.Warn("We approved Album: {0} but could not add it to Headphones because it has not been setup", request.Title);
|
||||
|
||||
// Update the record
|
||||
var inserted = await Service.UpdateRequestAsync(request);
|
||||
return Response.AsJson(inserted
|
||||
? new JsonResponseModel { Result = true, Message = "This has been approved, but It has not been sent to Headphones because it has not been configured." }
|
||||
: new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = "We could not approve this request. Please try again or check the logs."
|
||||
});
|
||||
}
|
||||
|
||||
var sender = new HeadphonesSender(HeadphoneApi, hpSettings, Service);
|
||||
var result = sender.AddAlbum(request);
|
||||
|
||||
|
||||
return Response.AsJson(new JsonResponseModel { Result = true, Message = "We have sent the approval to Headphones for processing, This can take a few minutes." });
|
||||
}
|
||||
|
||||
private async Task<Response> ApproveAllMovies()
|
||||
{
|
||||
|
||||
var requests = await Service.GetAllAsync();
|
||||
requests = requests.Where(x => x.CanApprove && x.Type == RequestType.Movie);
|
||||
var requestedModels = requests as RequestedModel[] ?? requests.ToArray();
|
||||
if (!requestedModels.Any())
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "There are no movie requests to approve. Please refresh." });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return await UpdateRequestsAsync(requestedModels);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Fatal(e);
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" });
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Response> DeleteAllMovies()
|
||||
{
|
||||
|
||||
var requests = await Service.GetAllAsync();
|
||||
requests = requests.Where(x => x.Type == RequestType.Movie);
|
||||
var requestedModels = requests as RequestedModel[] ?? requests.ToArray();
|
||||
if (!requestedModels.Any())
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "There are no movie requests to delete. Please refresh." });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return await DeleteRequestsAsync(requestedModels);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Fatal(e);
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" });
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Response> DeleteAllAlbums()
|
||||
{
|
||||
|
||||
var requests = await Service.GetAllAsync();
|
||||
requests = requests.Where(x => x.Type == RequestType.Album);
|
||||
var requestedModels = requests as RequestedModel[] ?? requests.ToArray();
|
||||
if (!requestedModels.Any())
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "There are no album requests to delete. Please refresh." });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return await DeleteRequestsAsync(requestedModels);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Fatal(e);
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" });
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Response> ApproveAllTVShows()
|
||||
{
|
||||
var requests = await Service.GetAllAsync();
|
||||
requests = requests.Where(x => x.CanApprove && x.Type == RequestType.TvShow);
|
||||
var requestedModels = requests as RequestedModel[] ?? requests.ToArray();
|
||||
if (!requestedModels.Any())
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "There are no tv show requests to approve. Please refresh." });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return await UpdateRequestsAsync(requestedModels);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Fatal(e);
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" });
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Response> DeleteAllTVShows()
|
||||
{
|
||||
|
||||
var requests = await Service.GetAllAsync();
|
||||
requests = requests.Where(x => x.Type == RequestType.TvShow);
|
||||
var requestedModels = requests as RequestedModel[] ?? requests.ToArray();
|
||||
if (!requestedModels.Any())
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "There are no tv show requests to delete. Please refresh." });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return await DeleteRequestsAsync(requestedModels);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Fatal(e);
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Approves all.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task<Response> ApproveAll()
|
||||
{
|
||||
var requests = await Service.GetAllAsync();
|
||||
requests = requests.Where(x => x.CanApprove);
|
||||
var requestedModels = requests as RequestedModel[] ?? requests.ToArray();
|
||||
if (!requestedModels.Any())
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "There are no requests to approve. Please refresh." });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return await UpdateRequestsAsync(requestedModels);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Fatal(e);
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private async Task<Response> DeleteRequestsAsync(IEnumerable<RequestedModel> requestedModels)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await Service.BatchDeleteAsync(requestedModels);
|
||||
return Response.AsJson(result
|
||||
? new JsonResponseModel { Result = true }
|
||||
: new JsonResponseModel { Result = false, Message = "We could not delete all of the requests. Please try again or check the logs." });
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Fatal(e);
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" });
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Response> UpdateRequestsAsync(RequestedModel[] requestedModels)
|
||||
{
|
||||
var cpSettings = await CpService.GetSettingsAsync();
|
||||
var updatedRequests = new List<RequestedModel>();
|
||||
foreach (var r in requestedModels)
|
||||
{
|
||||
if (r.Type == RequestType.Movie)
|
||||
{
|
||||
if (cpSettings.Enabled)
|
||||
{
|
||||
var res = SendMovie(cpSettings, r, CpApi);
|
||||
if (res)
|
||||
{
|
||||
r.Approved = true;
|
||||
updatedRequests.Add(r);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("Could not approve and send the movie {0} to couch potato!", r.Title);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
r.Approved = true;
|
||||
updatedRequests.Add(r);
|
||||
}
|
||||
}
|
||||
if (r.Type == RequestType.TvShow)
|
||||
{
|
||||
var sender = new TvSenderOld(SonarrApi, SickRageApi); // TODO put back
|
||||
var sr = await SickRageSettings.GetSettingsAsync();
|
||||
var sonarr = await SonarrSettings.GetSettingsAsync();
|
||||
if (sr.Enabled)
|
||||
{
|
||||
var res = sender.SendToSickRage(sr, r);
|
||||
if (res?.result == "success")
|
||||
{
|
||||
r.Approved = true;
|
||||
updatedRequests.Add(r);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("Could not approve and send the TV {0} to SickRage!", r.Title);
|
||||
Log.Error("SickRage Message: {0}", res?.message);
|
||||
}
|
||||
}
|
||||
|
||||
else if (sonarr.Enabled)
|
||||
{
|
||||
var res = await sender.SendToSonarr(sonarr, r);
|
||||
if (!string.IsNullOrEmpty(res?.title))
|
||||
{
|
||||
r.Approved = true;
|
||||
updatedRequests.Add(r);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("Could not approve and send the TV {0} to Sonarr!", r.Title);
|
||||
res?.ErrorMessages?.ForEach(x => Log.Error("Error messages: {0}", x));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
r.Approved = true;
|
||||
updatedRequests.Add(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
var result = await Service.BatchUpdateAsync(updatedRequests);
|
||||
return Response.AsJson(result
|
||||
? new JsonResponseModel { Result = true }
|
||||
: new JsonResponseModel { Result = false, Message = "We could not approve all of the requests. Please try again or check the logs." });
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Fatal(e);
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" });
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Response> DenyRequest(int requestId, string reason)
|
||||
{
|
||||
// Get the request from the DB
|
||||
var request = await Service.GetAsync(requestId);
|
||||
|
||||
// Deny it
|
||||
request.Denied = true;
|
||||
request.DeniedReason = reason;
|
||||
|
||||
// Update the new value
|
||||
var result = await Service.UpdateRequestAsync(request);
|
||||
|
||||
return result
|
||||
? Response.AsJson(new JsonResponseModel { Result = true, Message = "Request has been denied" })
|
||||
: Response.AsJson(new JsonResponseModel { Result = false, Message = "An error happened, could not update the DB" });
|
||||
|
||||
}
|
||||
|
||||
private bool SendMovie(CouchPotatoSettings settings, RequestedModel r, ICouchPotatoApi cp)
|
||||
{
|
||||
Log.Info("Adding movie to CP : {0}", r.Title);
|
||||
var result = cp.AddMovie(r.ImdbId, settings.ApiKey, r.Title, settings.FullUri, settings.ProfileId);
|
||||
Log.Trace("Adding movie to CP result {0}", result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
122
Ombi.UI/Modules/BaseApiModule.cs
Normal file
122
Ombi.UI/Modules/BaseApiModule.cs
Normal file
|
@ -0,0 +1,122 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: BaseApiModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Nancy;
|
||||
using Nancy.Validation;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Store;
|
||||
using Ombi.UI.Models;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public abstract class BaseApiModule : BaseModule
|
||||
{
|
||||
protected BaseApiModule(ISettingsService<PlexRequestSettings> s, ISecurityExtensions security) : base(s,security)
|
||||
{
|
||||
Settings = s;
|
||||
Before += (ctx) => CheckAuth();
|
||||
}
|
||||
|
||||
protected BaseApiModule(string modulePath, ISettingsService<PlexRequestSettings> s, ISecurityExtensions security) : base(modulePath, s, security)
|
||||
{
|
||||
Settings = s;
|
||||
Before += (ctx) => CheckAuth();
|
||||
}
|
||||
|
||||
private ISettingsService<PlexRequestSettings> Settings { get; }
|
||||
|
||||
protected Response ReturnReponse(object result)
|
||||
{
|
||||
var queryString = (DynamicDictionary)Context.Request.Query;
|
||||
dynamic value;
|
||||
if (queryString.TryGetValue("xml", out value))
|
||||
{
|
||||
if ((bool)value)
|
||||
{
|
||||
return Response.AsXml(result);
|
||||
}
|
||||
}
|
||||
return Response.AsJson(result);
|
||||
}
|
||||
|
||||
protected Response ReturnValidationReponse(ModelValidationResult result)
|
||||
{
|
||||
var errors = result.Errors;
|
||||
var model = new ApiModel<List<string>>
|
||||
{
|
||||
Error = true,
|
||||
ErrorMessage = "Please view the error messages inside the data node",
|
||||
Data = new List<string>()
|
||||
};
|
||||
|
||||
foreach (var error in errors)
|
||||
{
|
||||
model.Data.AddRange(error.Value.Select(x => x.ErrorMessage));
|
||||
}
|
||||
|
||||
return ReturnReponse(model);
|
||||
}
|
||||
|
||||
private Response CheckAuth()
|
||||
{
|
||||
if (Request.Path.Contains("api/apikey")) // We do not need the apikey for this call
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var settings = Settings.GetSettings();
|
||||
var apiModel = new ApiModel<List<RequestedModel>> { Data = new List<RequestedModel>() };
|
||||
if (!Authenticated(settings))
|
||||
{
|
||||
apiModel.Error = true;
|
||||
apiModel.ErrorMessage = "ApiKey is invalid or not present, Please use 'apikey' in the querystring.";
|
||||
return ReturnReponse(apiModel);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool Authenticated(PlexRequestSettings settings)
|
||||
{
|
||||
var query = (DynamicDictionary)Context.Request.Query;
|
||||
dynamic key;
|
||||
if (!query.TryGetValue("apikey", out key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if ((string)key == settings.ApiKey)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
78
Ombi.UI/Modules/BaseAuthModule.cs
Normal file
78
Ombi.UI/Modules/BaseAuthModule.cs
Normal file
|
@ -0,0 +1,78 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: BaseAuthModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
|
||||
#endregion
|
||||
|
||||
using Nancy;
|
||||
using Nancy.Extensions;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public abstract class BaseAuthModule : BaseModule
|
||||
{
|
||||
protected BaseAuthModule(ISettingsService<PlexRequestSettings> pr, ISecurityExtensions security) : base(pr,security)
|
||||
{
|
||||
PlexRequestSettings = pr;
|
||||
Before += (ctx) => CheckAuth();
|
||||
}
|
||||
|
||||
protected BaseAuthModule(string modulePath, ISettingsService<PlexRequestSettings> pr, ISecurityExtensions security) : base(modulePath, pr, security)
|
||||
{
|
||||
PlexRequestSettings = pr;
|
||||
Before += (ctx) => CheckAuth();
|
||||
}
|
||||
|
||||
protected ISettingsService<PlexRequestSettings> PlexRequestSettings { get; }
|
||||
|
||||
private Response CheckAuth()
|
||||
{
|
||||
var settings = PlexRequestSettings.GetSettings();
|
||||
|
||||
var baseUrl = settings.BaseUrl;
|
||||
|
||||
// Have we been through the wizard?
|
||||
if (!settings.Wizard)
|
||||
{
|
||||
return Context.GetRedirect(string.IsNullOrEmpty(baseUrl) ? "~/wizard" : $"~/{baseUrl}/wizard");
|
||||
}
|
||||
if (!Request.IsAjaxRequest())
|
||||
{
|
||||
var redirectPath = string.IsNullOrEmpty(baseUrl) ? "~/userlogin" : $"~/{baseUrl}/userlogin";
|
||||
|
||||
if (Session[SessionKeys.UsernameKey] == null && Context?.CurrentUser == null)
|
||||
{
|
||||
return Context.GetRedirect(redirectPath);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
189
Ombi.UI/Modules/BaseModule.cs
Normal file
189
Ombi.UI/Modules/BaseModule.cs
Normal file
|
@ -0,0 +1,189 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: BaseModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Nancy;
|
||||
using Nancy.Security;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Helpers.Permissions;
|
||||
using Ombi.UI.Helpers;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public abstract class BaseModule : NancyModule
|
||||
{
|
||||
protected string BaseUrl { get; set; }
|
||||
|
||||
|
||||
protected BaseModule(ISettingsService<PlexRequestSettings> settingsService, ISecurityExtensions security)
|
||||
{
|
||||
|
||||
var settings = settingsService.GetSettings();
|
||||
var baseUrl = settings.BaseUrl;
|
||||
BaseUrl = baseUrl;
|
||||
|
||||
var modulePath = string.IsNullOrEmpty(baseUrl) ? string.Empty : baseUrl;
|
||||
|
||||
ModulePath = modulePath;
|
||||
Security = security;
|
||||
|
||||
Before += (ctx) => SetCookie();
|
||||
}
|
||||
|
||||
protected BaseModule(string modulePath, ISettingsService<PlexRequestSettings> settingsService, ISecurityExtensions security)
|
||||
{
|
||||
|
||||
var settings = settingsService.GetSettings();
|
||||
var baseUrl = settings.BaseUrl;
|
||||
BaseUrl = baseUrl;
|
||||
|
||||
var settingModulePath = string.IsNullOrEmpty(baseUrl) ? modulePath : $"{baseUrl}/{modulePath}";
|
||||
|
||||
ModulePath = settingModulePath;
|
||||
Security = security;
|
||||
|
||||
Before += (ctx) =>
|
||||
{
|
||||
SetCookie();
|
||||
|
||||
if (!string.IsNullOrEmpty(ctx.Request.Session["TempMessage"] as string))
|
||||
{
|
||||
ctx.ViewBag.TempMessage = ctx.Request.Session["TempMessage"];
|
||||
ctx.ViewBag.TempType = ctx.Request.Session["TempType"];
|
||||
ctx.Request.Session.DeleteAll();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
private int _dateTimeOffset = -1;
|
||||
protected int DateTimeOffset
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_dateTimeOffset == -1)
|
||||
{
|
||||
_dateTimeOffset = (int?)Session[SessionKeys.ClientDateTimeOffsetKey] ?? new DateTimeOffset().Offset.Minutes;
|
||||
}
|
||||
return _dateTimeOffset;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private string _username;
|
||||
/// <summary>
|
||||
/// Returns the Username or UserAlias
|
||||
/// </summary>
|
||||
protected string Username
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_username))
|
||||
{
|
||||
try
|
||||
{
|
||||
var username = Security.GetUsername(User.UserName, Session);
|
||||
if (string.IsNullOrEmpty(username))
|
||||
{
|
||||
return Session[SessionKeys.UsernameKey].ToString();
|
||||
}
|
||||
_username = username;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
return _username;
|
||||
}
|
||||
}
|
||||
|
||||
protected IDictionary<string, string> Cookies => Request?.Cookies;
|
||||
|
||||
protected bool IsAdmin
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!LoggedIn)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Security.HasPermissions(Context?.CurrentUser, Permissions.Administrator);
|
||||
}
|
||||
}
|
||||
|
||||
protected IUserIdentity User => Context?.CurrentUser;
|
||||
|
||||
protected ISecurityExtensions Security { get; set; }
|
||||
|
||||
protected bool LoggedIn => Context?.CurrentUser != null;
|
||||
|
||||
protected string Culture { get; set; }
|
||||
protected const string CultureCookieName = "_culture";
|
||||
protected Response SetCookie()
|
||||
{
|
||||
try
|
||||
{
|
||||
string cultureName;
|
||||
|
||||
// Attempt to read the culture cookie from Request
|
||||
var outCookie = string.Empty;
|
||||
if (Cookies.TryGetValue(CultureCookieName, out outCookie))
|
||||
{
|
||||
cultureName = outCookie;
|
||||
}
|
||||
else
|
||||
{
|
||||
cultureName = Request.Headers?.AcceptLanguage?.FirstOrDefault()?.Item1;
|
||||
}
|
||||
|
||||
// Validate culture name
|
||||
cultureName = CultureHelper.GetImplementedCulture(cultureName); // This is safe
|
||||
|
||||
|
||||
// Modify current thread's cultures
|
||||
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureName);
|
||||
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
|
||||
|
||||
Culture = Thread.CurrentThread.CurrentCulture.Name;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Couldn't Set the culture
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
39
Ombi.UI/Modules/BetaModule.cs
Normal file
39
Ombi.UI/Modules/BetaModule.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: BetaModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using Nancy;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class BetaModule : NancyModule
|
||||
{
|
||||
public BetaModule() : base("beta")
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
79
Ombi.UI/Modules/CultureModule.cs
Normal file
79
Ombi.UI/Modules/CultureModule.cs
Normal file
|
@ -0,0 +1,79 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: CultureModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using Nancy;
|
||||
using Nancy.Extensions;
|
||||
using Nancy.Responses;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Helpers.Analytics;
|
||||
using Ombi.UI.Helpers;
|
||||
using Action = Ombi.Helpers.Analytics.Action;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class CultureModule : BaseModule
|
||||
{
|
||||
public CultureModule(ISettingsService<PlexRequestSettings> pr, IAnalytics a, ISecurityExtensions security) : base("culture",pr, security)
|
||||
{
|
||||
Analytics = a;
|
||||
|
||||
Get["/"] = x => SetCulture();
|
||||
}
|
||||
|
||||
private IAnalytics Analytics { get; }
|
||||
|
||||
private RedirectResponse SetCulture()
|
||||
{
|
||||
var culture = (string)Request.Query["l"];
|
||||
var returnUrl = (string)Request.Query["u"];
|
||||
|
||||
// Validate
|
||||
culture = CultureHelper.GetImplementedCulture(culture);
|
||||
|
||||
var outCookie = string.Empty;
|
||||
if (Cookies.TryGetValue(CultureCookieName, out outCookie))
|
||||
{
|
||||
Cookies[CultureCookieName] = culture;
|
||||
}
|
||||
else
|
||||
{
|
||||
Cookies.Add(CultureCookieName, culture);
|
||||
}
|
||||
var cookie = Cookies[CultureCookieName];
|
||||
var response = Context.GetRedirect(returnUrl);
|
||||
|
||||
response.WithCookie(CultureCookieName, cookie ?? culture, DateTime.Now.AddYears(1));
|
||||
Analytics.TrackEventAsync(Category.Navbar, Action.Language, culture, Username, CookieHelper.GetAnalyticClientId(Cookies));
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
45
Ombi.UI/Modules/DonationLinkModule.cs
Normal file
45
Ombi.UI/Modules/DonationLinkModule.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Nancy;
|
||||
using NLog;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class DonationLinkModule : BaseAuthModule
|
||||
{
|
||||
public DonationLinkModule(ICacheProvider provider, ISettingsService<PlexRequestSettings> pr, ISecurityExtensions security) : base("customDonation", pr, security)
|
||||
{
|
||||
Cache = provider;
|
||||
Get["/", true] = async (x, ct) => await GetCustomDonationUrl(pr);
|
||||
}
|
||||
|
||||
private ICacheProvider Cache { get; }
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private async Task<Response> GetCustomDonationUrl(ISettingsService<PlexRequestSettings> pr)
|
||||
{
|
||||
PlexRequestSettings settings = await pr.GetSettingsAsync();
|
||||
try
|
||||
{
|
||||
if (settings.EnableCustomDonationUrl && Security.IsLoggedIn(Context))
|
||||
{
|
||||
return Response.AsJson(new { url = settings.CustomDonationUrl, message = settings.CustomDonationMessage, enabled = true });
|
||||
}
|
||||
|
||||
return Response.AsJson(new { enabled = false });
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Warn("Exception Thrown when attempting to check the custom donation url");
|
||||
Log.Warn(e);
|
||||
return Response.AsJson(new { url = settings.CustomDonationUrl, message = settings.CustomDonationMessage });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
80
Ombi.UI/Modules/IndexModule.cs
Normal file
80
Ombi.UI/Modules/IndexModule.cs
Normal file
|
@ -0,0 +1,80 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: IndexModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Nancy.Extensions;
|
||||
using Nancy.Linker;
|
||||
using Nancy.Responses;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class IndexModule : BaseAuthModule
|
||||
{
|
||||
public IndexModule(ISettingsService<PlexRequestSettings> pr, ISettingsService<LandingPageSettings> l, IResourceLinker rl, ISecurityExtensions security) : base(pr, security)
|
||||
{
|
||||
LandingPage = l;
|
||||
Linker = rl;
|
||||
Get["Index", "/", true] = async (x, ct) => await Index();
|
||||
|
||||
Get["/Index", true] = async (x, ct) => await Index();
|
||||
}
|
||||
private ISettingsService<LandingPageSettings> LandingPage { get; }
|
||||
private IResourceLinker Linker { get; }
|
||||
|
||||
public async Task<RedirectResponse> Index()
|
||||
{
|
||||
var settings = await LandingPage.GetSettingsAsync();
|
||||
if (settings.Enabled)
|
||||
{
|
||||
if (settings.BeforeLogin) // Before login
|
||||
{
|
||||
if (string.IsNullOrEmpty(Username))
|
||||
{
|
||||
// They are not logged in
|
||||
return Context.GetRedirect(Linker.BuildRelativeUri(Context, "LandingPageIndex").ToString());
|
||||
}
|
||||
return Context.GetRedirect(Linker.BuildRelativeUri(Context, "SearchIndex").ToString());
|
||||
}
|
||||
|
||||
// After login
|
||||
if (string.IsNullOrEmpty(Username))
|
||||
{
|
||||
// Not logged in yet
|
||||
return Context.GetRedirect(Linker.BuildRelativeUri(Context, "UserLoginIndex").ToString());
|
||||
}
|
||||
// Send them to landing
|
||||
var landingUrl = Linker.BuildRelativeUri(Context, "LandingPageIndex").ToString();
|
||||
return Context.GetRedirect(landingUrl);
|
||||
}
|
||||
|
||||
return Context.GetRedirect(Linker.BuildRelativeUri(Context, "UserLoginIndex").ToString());
|
||||
}
|
||||
}
|
||||
}
|
478
Ombi.UI/Modules/IssuesModule.cs
Normal file
478
Ombi.UI/Modules/IssuesModule.cs
Normal file
|
@ -0,0 +1,478 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Nancy;
|
||||
using Nancy.Responses.Negotiation;
|
||||
using NLog;
|
||||
using Ombi.Api;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.Models;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Helpers.Permissions;
|
||||
using Ombi.Services.Interfaces;
|
||||
using Ombi.Services.Notification;
|
||||
using Ombi.Store;
|
||||
using Ombi.UI.Models;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class IssuesModule : BaseAuthModule
|
||||
{
|
||||
public IssuesModule(ISettingsService<PlexRequestSettings> pr, IIssueService issueService, IRequestService request, INotificationService n, ISecurityExtensions security) : base("issues", pr, security)
|
||||
{
|
||||
IssuesService = issueService;
|
||||
RequestService = request;
|
||||
NotificationService = n;
|
||||
|
||||
Get["/"] = x => Index();
|
||||
|
||||
Get["/{id}", true] = async (x, ct) => await Details(x.id);
|
||||
|
||||
Post["/issue", true] = async (x, ct) => await ReportRequestIssue((int)Request.Form.requestId, (IssueState)(int)Request.Form.issue, null);
|
||||
|
||||
Get["/pending", true] = async (x, ct) => await GetIssues(IssueStatus.PendingIssue);
|
||||
Get["/resolved", true] = async (x, ct) => await GetIssues(IssueStatus.ResolvedIssue);
|
||||
|
||||
Post["/remove", true] = async (x, ct) => await RemoveIssue((int)Request.Form.issueId);
|
||||
Post["/resolvedUpdate", true] = async (x, ct) => await ChangeStatus((int)Request.Form.issueId, IssueStatus.ResolvedIssue);
|
||||
|
||||
Post["/clear", true] = async (x, ct) => await ClearIssue((int)Request.Form.issueId, (IssueState)(int)Request.Form.issue);
|
||||
|
||||
Get["/issuecount", true] = async (x, ct) => await IssueCount();
|
||||
Get["/tabCount", true] = async (x, ct) => await TabCount();
|
||||
|
||||
Post["/issuecomment", true] = async (x, ct) => await ReportRequestIssue((int)Request.Form.providerId, IssueState.Other, (string)Request.Form.commentArea);
|
||||
|
||||
Post["/nonrequestissue", true] = async (x, ct) => await ReportNonRequestIssue((int)Request.Form.providerId, (string)Request.Form.type, (IssueState)(int)Request.Form.issue, null);
|
||||
|
||||
Post["/nonrequestissuecomment", true] = async (x, ct) => await ReportNonRequestIssue((int)Request.Form.providerId, (string)Request.Form.type, IssueState.Other, (string)Request.Form.commentArea);
|
||||
|
||||
|
||||
Post["/addnote", true] = async (x, ct) => await AddNote((int)Request.Form.requestId, (string)Request.Form.noteArea, (IssueState)(int)Request.Form.issue);
|
||||
}
|
||||
|
||||
private IIssueService IssuesService { get; }
|
||||
private IRequestService RequestService { get; }
|
||||
private INotificationService NotificationService { get; }
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public Negotiator Index()
|
||||
{
|
||||
return View["Index"];
|
||||
}
|
||||
|
||||
private async Task<Response> GetIssues(IssueStatus status)
|
||||
{
|
||||
var issues = await IssuesService.GetAllAsync();
|
||||
issues = await FilterIssuesAsync(issues, status == IssueStatus.ResolvedIssue);
|
||||
|
||||
var issuesModels = issues as IssuesModel[] ?? issues.Where(x => x.IssueStatus == status).ToArray();
|
||||
var viewModel = new List<IssuesViewModel>();
|
||||
|
||||
foreach (var i in issuesModels)
|
||||
{
|
||||
var model = new IssuesViewModel { Id = i.Id, RequestId = i.RequestId, Title = i.Title, Type = i.Type.ToString().ToCamelCaseWords(), Admin = Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ManageRequests)
|
||||
};
|
||||
|
||||
// Create a string with all of the current issue states with a "," delimiter in e.g. Wrong Content, Playback Issues
|
||||
var state = i.Issues.Select(x => x.Issue).ToArray();
|
||||
var issueState = string.Empty;
|
||||
for (var j = 0; j < state.Length; j++)
|
||||
{
|
||||
var word = state[j].ToString().ToCamelCaseWords();
|
||||
if (j != state.Length - 1)
|
||||
{
|
||||
issueState += $"{word}, ";
|
||||
}
|
||||
else
|
||||
{
|
||||
issueState += word;
|
||||
}
|
||||
}
|
||||
model.Issues = issueState;
|
||||
|
||||
viewModel.Add(model);
|
||||
}
|
||||
|
||||
return Response.AsJson(viewModel);
|
||||
}
|
||||
|
||||
public async Task<Response> IssueCount()
|
||||
{
|
||||
var issues = await IssuesService.GetAllAsync();
|
||||
|
||||
var myIssues = await FilterIssuesAsync(issues);
|
||||
|
||||
var count = myIssues.Count();
|
||||
|
||||
return Response.AsJson(count);
|
||||
}
|
||||
|
||||
public async Task<Response> TabCount()
|
||||
{
|
||||
var issues = await IssuesService.GetAllAsync();
|
||||
|
||||
var myIssues = await FilterIssuesAsync(issues);
|
||||
|
||||
var count = new List<object>();
|
||||
|
||||
var issuesModels = myIssues as IssuesModel[] ?? myIssues.ToArray();
|
||||
var pending = issuesModels.Where(x => x.IssueStatus == IssueStatus.PendingIssue);
|
||||
var resolved = issuesModels.Where(x => x.IssueStatus == IssueStatus.ResolvedIssue);
|
||||
|
||||
count.Add(new { Name = IssueStatus.PendingIssue, Count = pending.Count() });
|
||||
count.Add(new { Name = IssueStatus.ResolvedIssue, Count = resolved.Count() });
|
||||
|
||||
return Response.AsJson(count);
|
||||
}
|
||||
|
||||
public async Task<Negotiator> Details(int id)
|
||||
{
|
||||
var issue = await IssuesService.GetAsync(id);
|
||||
if (issue == null)
|
||||
return Index();
|
||||
|
||||
issue = Order(issue);
|
||||
var m = new IssuesDetailsViewModel
|
||||
{
|
||||
Issues = issue.Issues,
|
||||
RequestId = issue.RequestId,
|
||||
Title = issue.Title,
|
||||
IssueStatus = issue.IssueStatus,
|
||||
Deleted = issue.Deleted,
|
||||
Type = issue.Type,
|
||||
ProviderId = issue.ProviderId,
|
||||
PosterUrl = issue.PosterUrl,
|
||||
Id = issue.Id
|
||||
};
|
||||
return View["Details", m];
|
||||
}
|
||||
|
||||
private async Task<Response> ReportRequestIssue(int requestId, IssueState issue, string comment)
|
||||
{
|
||||
|
||||
var model = new IssueModel
|
||||
{
|
||||
Issue = issue,
|
||||
UserReported = Username,
|
||||
UserNote = !string.IsNullOrEmpty(comment)
|
||||
? $"{Username} - {comment}"
|
||||
: string.Empty,
|
||||
};
|
||||
|
||||
var request = await RequestService.GetAsync(requestId);
|
||||
|
||||
var issueEntity = await IssuesService.GetAllAsync();
|
||||
var existingIssue = issueEntity.FirstOrDefault(x => x.RequestId == requestId);
|
||||
|
||||
var notifyModel = new NotificationModel
|
||||
{
|
||||
User = Username,
|
||||
NotificationType = NotificationType.Issue,
|
||||
Title = request.Title,
|
||||
DateTime = DateTime.Now,
|
||||
Body = issue == IssueState.Other ? comment : issue.ToString().ToCamelCaseWords()
|
||||
};
|
||||
|
||||
// An issue already exists
|
||||
if (existingIssue != null)
|
||||
{
|
||||
if (existingIssue.Issues.Any(x => x.Issue == issue))
|
||||
{
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = "This issue has already been reported!"
|
||||
});
|
||||
|
||||
}
|
||||
existingIssue.Issues.Add(model);
|
||||
var result = await IssuesService.UpdateIssueAsync(existingIssue);
|
||||
|
||||
|
||||
await NotificationService.Publish(notifyModel);
|
||||
|
||||
return Response.AsJson(result
|
||||
? new JsonResponseModel { Result = true }
|
||||
: new JsonResponseModel { Result = false });
|
||||
}
|
||||
|
||||
// New issue
|
||||
var issues = new IssuesModel
|
||||
{
|
||||
Title = request.Title,
|
||||
PosterUrl = request.PosterPath,
|
||||
RequestId = requestId,
|
||||
Type = request.Type,
|
||||
IssueStatus = IssueStatus.PendingIssue
|
||||
};
|
||||
issues.Issues.Add(model);
|
||||
|
||||
var issueId = await IssuesService.AddIssueAsync(issues);
|
||||
|
||||
request.IssueId = issueId;
|
||||
await RequestService.UpdateRequestAsync(request);
|
||||
|
||||
await NotificationService.Publish(notifyModel);
|
||||
return Response.AsJson(new JsonResponseModel { Result = true });
|
||||
}
|
||||
|
||||
private async Task<Response> ReportNonRequestIssue(int providerId, string type, IssueState issue, string comment)
|
||||
{
|
||||
var currentIssues = await IssuesService.GetAllAsync();
|
||||
var notifyModel = new NotificationModel
|
||||
{
|
||||
User = Username,
|
||||
NotificationType = NotificationType.Issue,
|
||||
DateTime = DateTime.Now,
|
||||
Body = issue == IssueState.Other ? comment : issue.ToString().ToCamelCaseWords()
|
||||
};
|
||||
var model = new IssueModel
|
||||
{
|
||||
Issue = issue,
|
||||
UserReported = Username,
|
||||
UserNote = !string.IsNullOrEmpty(comment)
|
||||
? $"{Username} - {comment}"
|
||||
: string.Empty,
|
||||
};
|
||||
|
||||
var existing = currentIssues.FirstOrDefault(x => x.ProviderId == providerId && !x.Deleted && x.IssueStatus == IssueStatus.PendingIssue);
|
||||
if (existing != null)
|
||||
{
|
||||
existing.Issues.Add(model);
|
||||
await IssuesService.UpdateIssueAsync(existing);
|
||||
return Response.AsJson(new JsonResponseModel { Result = true });
|
||||
}
|
||||
|
||||
if (type == "movie")
|
||||
{
|
||||
var movieApi = new TheMovieDbApi();
|
||||
|
||||
var result = await movieApi.GetMovieInformation(providerId);
|
||||
if (result != null)
|
||||
{
|
||||
notifyModel.Title = result.Title;
|
||||
// New issue
|
||||
var issues = new IssuesModel
|
||||
{
|
||||
Title = result.Title,
|
||||
PosterUrl = "https://image.tmdb.org/t/p/w150/" + result.PosterPath,
|
||||
ProviderId = providerId,
|
||||
Type = RequestType.Movie,
|
||||
IssueStatus = IssueStatus.PendingIssue
|
||||
};
|
||||
issues.Issues.Add(model);
|
||||
|
||||
var issueId = await IssuesService.AddIssueAsync(issues);
|
||||
|
||||
await NotificationService.Publish(notifyModel);
|
||||
return Response.AsJson(new JsonResponseModel { Result = true });
|
||||
}
|
||||
}
|
||||
|
||||
if (type == "tv")
|
||||
{
|
||||
var tv = new TvMazeApi();
|
||||
var result = tv.ShowLookupByTheTvDbId(providerId);
|
||||
if (result != null)
|
||||
{
|
||||
var banner = result.image?.medium;
|
||||
if (!string.IsNullOrEmpty(banner))
|
||||
{
|
||||
banner = banner.Replace("http", "https");
|
||||
}
|
||||
|
||||
notifyModel.Title = result.name;
|
||||
// New issue
|
||||
var issues = new IssuesModel
|
||||
{
|
||||
Title = result.name,
|
||||
PosterUrl = banner,
|
||||
ProviderId = providerId,
|
||||
Type = RequestType.TvShow,
|
||||
IssueStatus = IssueStatus.PendingIssue
|
||||
};
|
||||
issues.Issues.Add(model);
|
||||
|
||||
var issueId = await IssuesService.AddIssueAsync(issues);
|
||||
|
||||
await NotificationService.Publish(notifyModel);
|
||||
return Response.AsJson(new JsonResponseModel { Result = true });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Album Reports are not supported yet!"});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filters the issues. Checks to see if we have set <c>UsersCanViewOnlyOwnIssues</c> in the database and filters upon the user logged in and that setting.
|
||||
/// </summary>
|
||||
/// <param name="issues">The issues.</param>
|
||||
private async Task<IEnumerable<IssuesModel>> FilterIssuesAsync(IEnumerable<IssuesModel> issues, bool showResolved = false)
|
||||
{
|
||||
var settings = await PlexRequestSettings.GetSettingsAsync();
|
||||
IEnumerable<IssuesModel> myIssues;
|
||||
|
||||
// Is the user an Admin? If so show everything
|
||||
if (IsAdmin)
|
||||
{
|
||||
var issuesModels = issues as IssuesModel[] ?? issues.ToArray();
|
||||
myIssues = issuesModels.Where(x => x.Deleted == false);
|
||||
if (!showResolved)
|
||||
{
|
||||
myIssues = issuesModels.Where(x => x.IssueStatus != IssueStatus.ResolvedIssue);
|
||||
}
|
||||
}
|
||||
else if (Security.HasPermissions(User, Permissions.UsersCanViewOnlyOwnIssues)) // The user is not an Admin, do we have the settings to hide them?
|
||||
{
|
||||
if (!showResolved)
|
||||
{
|
||||
myIssues =
|
||||
issues.Where(
|
||||
x =>
|
||||
x.Issues.Any(i => i.UserReported.Equals(Username, StringComparison.CurrentCultureIgnoreCase)) && x.Deleted == false
|
||||
&& x.IssueStatus != IssueStatus.ResolvedIssue);
|
||||
}
|
||||
else
|
||||
{
|
||||
myIssues =
|
||||
issues.Where(
|
||||
x =>
|
||||
x.Issues.Any(i => i.UserReported.Equals(Username, StringComparison.CurrentCultureIgnoreCase)) && x.Deleted == false);
|
||||
}
|
||||
}
|
||||
else // Looks like the user is not an admin and there is no settings set.
|
||||
{
|
||||
var issuesModels = issues as IssuesModel[] ?? issues.ToArray();
|
||||
myIssues = issuesModels.Where(x => x.Deleted == false);
|
||||
if (!showResolved)
|
||||
{
|
||||
myIssues = issuesModels.Where(x => x.IssueStatus != IssueStatus.ResolvedIssue);
|
||||
}
|
||||
}
|
||||
|
||||
return myIssues;
|
||||
}
|
||||
private async Task<Response> RemoveIssue(int issueId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ManageRequests))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Sorry, you do not have the correct permissions to remove an issue." });
|
||||
}
|
||||
|
||||
var issue = await IssuesService.GetAsync(issueId);
|
||||
var request = await RequestService.GetAsync(issue.RequestId);
|
||||
if (request.Id > 0)
|
||||
{
|
||||
request.IssueId = 0; // No issue;
|
||||
|
||||
var result = await RequestService.UpdateRequestAsync(request);
|
||||
if (result)
|
||||
{
|
||||
await IssuesService.DeleteIssueAsync(issueId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await IssuesService.DeleteIssueAsync(issueId);
|
||||
}
|
||||
|
||||
return Response.AsJson(new JsonResponseModel { Result = true });
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return Response.AsJson(new JsonResponseModel() { Result = false, Message = "Could not delete issue! Check the logs."});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private async Task<Negotiator> ChangeStatus(int issueId, IssueStatus status)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ManageRequests))
|
||||
{
|
||||
return View["Index"];
|
||||
}
|
||||
|
||||
|
||||
var issue = await IssuesService.GetAsync(issueId);
|
||||
issue.IssueStatus = status;
|
||||
var result = await IssuesService.UpdateIssueAsync(issue);
|
||||
return result ? await Details(issueId) : View["Index"];
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return View["Index"];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private async Task<Negotiator> ClearIssue(int issueId, IssueState state)
|
||||
{
|
||||
if (!Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ManageRequests))
|
||||
{
|
||||
return View["Index"];
|
||||
}
|
||||
|
||||
var issue = await IssuesService.GetAsync(issueId);
|
||||
|
||||
var toRemove = issue.Issues.FirstOrDefault(x => x.Issue == state);
|
||||
issue.Issues.Remove(toRemove);
|
||||
|
||||
var result = await IssuesService.UpdateIssueAsync(issue);
|
||||
|
||||
return result ? await Details(issueId) : View["Index"];
|
||||
}
|
||||
|
||||
private async Task<Response> AddNote(int requestId, string noteArea, IssueState state)
|
||||
{
|
||||
if (!Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ManageRequests))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Sorry, you do not have the correct permissions to add a note." });
|
||||
}
|
||||
|
||||
var issue = await IssuesService.GetAsync(requestId);
|
||||
if (issue == null)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Issue does not exist to add a note!" });
|
||||
}
|
||||
var toAddNote = issue.Issues.FirstOrDefault(x => x.Issue == state);
|
||||
|
||||
if (toAddNote != null)
|
||||
{
|
||||
issue.Issues.Remove(toAddNote);
|
||||
toAddNote.AdminNote = noteArea;
|
||||
issue.Issues.Add(toAddNote);
|
||||
}
|
||||
|
||||
var result = await IssuesService.UpdateIssueAsync(issue);
|
||||
return Response.AsJson(result
|
||||
? new JsonResponseModel { Result = true }
|
||||
: new JsonResponseModel { Result = false, Message = "Could not update the notes, please try again or check the logs" });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Orders the issues descending by the <see cref="IssueState"/>.
|
||||
/// </summary>
|
||||
/// <param name="issues">The issues.</param>
|
||||
/// <returns></returns>
|
||||
private IssuesModel Order(IssuesModel issues)
|
||||
{
|
||||
issues.Issues = issues.Issues.OrderByDescending(x => x.Issue).ToList();
|
||||
return issues;
|
||||
}
|
||||
}
|
||||
}
|
100
Ombi.UI/Modules/LandingPageModule.cs
Normal file
100
Ombi.UI/Modules/LandingPageModule.cs
Normal file
|
@ -0,0 +1,100 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: LandingPageModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Nancy;
|
||||
using Nancy.Linker;
|
||||
using Ombi.Api.Interfaces;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.UI.Models;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class LandingPageModule : BaseModule
|
||||
{
|
||||
public LandingPageModule(ISettingsService<PlexRequestSettings> settingsService, ISettingsService<LandingPageSettings> landing,
|
||||
ISettingsService<PlexSettings> ps, IPlexApi pApi, IResourceLinker linker, ISecurityExtensions security) : base("landing", settingsService, security)
|
||||
{
|
||||
LandingSettings = landing;
|
||||
PlexSettings = ps;
|
||||
PlexApi = pApi;
|
||||
Linker = linker;
|
||||
|
||||
Get["LandingPageIndex","/", true] = async (x, ct) =>
|
||||
{
|
||||
var s = await LandingSettings.GetSettingsAsync();
|
||||
if (!s.BeforeLogin && string.IsNullOrEmpty(Username)) //We are signed in
|
||||
{
|
||||
var url = Linker.BuildRelativeUri(Context, "SearchIndex").ToString();
|
||||
return Response.AsRedirect(url);
|
||||
}
|
||||
|
||||
var model = new LandingPageViewModel
|
||||
{
|
||||
Enabled = s.Enabled,
|
||||
Id = s.Id,
|
||||
EnabledNoticeTime = s.EnabledNoticeTime,
|
||||
NoticeEnable = s.NoticeEnable,
|
||||
NoticeEnd = s.NoticeEnd,
|
||||
NoticeMessage = s.NoticeMessage,
|
||||
NoticeStart = s.NoticeStart,
|
||||
ContinueUrl = s.BeforeLogin ? $"userlogin" : $"search"
|
||||
};
|
||||
return View["Landing/Index", model];
|
||||
};
|
||||
Get["/status", true] = async (x, ct) => await CheckStatus();
|
||||
|
||||
}
|
||||
|
||||
private ISettingsService<LandingPageSettings> LandingSettings { get; }
|
||||
private ISettingsService<PlexSettings> PlexSettings { get; }
|
||||
private IPlexApi PlexApi { get; }
|
||||
private IResourceLinker Linker { get; }
|
||||
|
||||
private async Task<Response> CheckStatus()
|
||||
{
|
||||
var plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
if (string.IsNullOrEmpty(plexSettings.PlexAuthToken) || string.IsNullOrEmpty(plexSettings.Ip))
|
||||
{
|
||||
return Response.AsJson(false);
|
||||
}
|
||||
try
|
||||
{
|
||||
var status = PlexApi.GetStatus(plexSettings.PlexAuthToken, plexSettings.FullUri);
|
||||
return Response.AsJson(status != null);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return Response.AsJson(false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
119
Ombi.UI/Modules/LayoutModule.cs
Normal file
119
Ombi.UI/Modules/LayoutModule.cs
Normal file
|
@ -0,0 +1,119 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: LayoutModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Nancy;
|
||||
using NLog;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Core.StatusChecker;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Services.Interfaces;
|
||||
using Ombi.Services.Jobs;
|
||||
using Ombi.UI.Models;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class LayoutModule : BaseAuthModule
|
||||
{
|
||||
public LayoutModule(ICacheProvider provider, ISettingsService<PlexRequestSettings> pr, ISettingsService<SystemSettings> settings, IJobRecord rec, ISecurityExtensions security) : base("layout", pr, security)
|
||||
{
|
||||
Cache = provider;
|
||||
SystemSettings = settings;
|
||||
Job = rec;
|
||||
|
||||
Get["/", true] = async (x,ct) => await CheckLatestVersion();
|
||||
Get["/cacher", true] = async (x,ct) => await CacherRunning();
|
||||
}
|
||||
|
||||
private ICacheProvider Cache { get; }
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
private ISettingsService<SystemSettings> SystemSettings { get; }
|
||||
private IJobRecord Job { get; }
|
||||
|
||||
private async Task<Response> CheckLatestVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!IsAdmin)
|
||||
{
|
||||
return Response.AsJson(new JsonUpdateAvailableModel { UpdateAvailable = false });
|
||||
}
|
||||
//#if DEBUG
|
||||
//return Response.AsJson(new JsonUpdateAvailableModel {UpdateAvailable = false});
|
||||
//#endif
|
||||
var checker = new StatusChecker(SystemSettings);
|
||||
var release = await Cache.GetOrSetAsync(CacheKeys.LastestProductVersion, async() => await checker.GetStatus(), 30);
|
||||
|
||||
return Response.AsJson(release.UpdateAvailable
|
||||
? new JsonUpdateAvailableModel { UpdateAvailable = true}
|
||||
: new JsonUpdateAvailableModel { UpdateAvailable = false });
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Warn("Exception Thrown when attempting to check the status");
|
||||
Log.Warn(e);
|
||||
return Response.AsJson(new JsonUpdateAvailableModel { UpdateAvailable = false });
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Response> CacherRunning()
|
||||
{
|
||||
try
|
||||
{
|
||||
var jobs = await Job.GetJobsAsync();
|
||||
|
||||
// Check to see if any are running
|
||||
var runningJobs = jobs.Where(x => x.Running);
|
||||
|
||||
// We only want the cachers
|
||||
var cacherJobs = runningJobs.Where(x =>
|
||||
x.Name.Equals(JobNames.CpCacher)
|
||||
|| x.Name.Equals(JobNames.EpisodeCacher)
|
||||
|| x.Name.Equals(JobNames.PlexChecker)
|
||||
|| x.Name.Equals(JobNames.SonarrCacher)
|
||||
|| x.Name.Equals(JobNames.SrCacher)
|
||||
|| x.Name.Equals(JobNames.PlexCacher));
|
||||
|
||||
|
||||
return Response.AsJson(cacherJobs.Any()
|
||||
? new { CurrentlyRunning = true, IsAdmin}
|
||||
: new { CurrentlyRunning = false, IsAdmin });
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Warn("Exception Thrown when attempting to check the status");
|
||||
Log.Warn(e);
|
||||
return Response.AsJson(new { CurrentlyRunning = false, IsAdmin });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
189
Ombi.UI/Modules/LoginModule.cs
Normal file
189
Ombi.UI/Modules/LoginModule.cs
Normal file
|
@ -0,0 +1,189 @@
|
|||
#region Copyright
|
||||
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: LoginModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Dynamic;
|
||||
using System.Security;
|
||||
using Nancy;
|
||||
using Nancy.Extensions;
|
||||
using Nancy.Linker;
|
||||
using Nancy.Responses.Negotiation;
|
||||
using Nancy.Security;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Helpers.Permissions;
|
||||
using Ombi.Store;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.UI.Authentication;
|
||||
using Ombi.UI.Models;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class LoginModule : BaseModule
|
||||
{
|
||||
public LoginModule(ISettingsService<PlexRequestSettings> pr, ICustomUserMapper m, IResourceLinker linker, IRepository<UserLogins> userLoginRepo, ISecurityExtensions security)
|
||||
: base(pr, security)
|
||||
{
|
||||
UserMapper = m;
|
||||
Get["LocalLogin","/login"] = _ =>
|
||||
{
|
||||
if (LoggedIn)
|
||||
{
|
||||
var url = linker.BuildRelativeUri(Context, "SearchIndex");
|
||||
return Response.AsRedirect(url.ToString());
|
||||
}
|
||||
dynamic model = new ExpandoObject();
|
||||
model.Redirect = Request.Query.redirect.Value ?? string.Empty;
|
||||
model.Errored = Request.Query.error.HasValue;
|
||||
var adminCreated = UserMapper.DoUsersExist();
|
||||
model.AdminExists = adminCreated;
|
||||
return View["Index", model];
|
||||
};
|
||||
|
||||
Get["/logout"] = x =>
|
||||
{
|
||||
if (Session[SessionKeys.UsernameKey] != null)
|
||||
{
|
||||
Session.Delete(SessionKeys.UsernameKey);
|
||||
}
|
||||
return CustomModuleExtensions.LogoutAndRedirect(this, !string.IsNullOrEmpty(BaseUrl) ? $"~/{BaseUrl}/" : "~/");
|
||||
};
|
||||
|
||||
Post["/login"] = x =>
|
||||
{
|
||||
var username = (string)Request.Form.Username;
|
||||
var password = (string)Request.Form.Password;
|
||||
var dtOffset = (int)Request.Form.DateTimeOffset;
|
||||
var redirect = (string)Request.Form.Redirect;
|
||||
|
||||
var userId = UserMapper.ValidateUser(username, password);
|
||||
|
||||
if (userId == null)
|
||||
{
|
||||
return
|
||||
Context.GetRedirect(!string.IsNullOrEmpty(BaseUrl)
|
||||
? $"~/{BaseUrl}/login?error=true&username=" + username
|
||||
: "~/login?error=true&username=" + username);
|
||||
}
|
||||
DateTime? expiry = null;
|
||||
if (Request.Form.RememberMe.HasValue)
|
||||
{
|
||||
expiry = DateTime.Now.AddDays(7);
|
||||
}
|
||||
Session[SessionKeys.UsernameKey] = username;
|
||||
Session[SessionKeys.ClientDateTimeOffsetKey] = dtOffset;
|
||||
if (redirect.Contains("userlogin"))
|
||||
{
|
||||
redirect = !string.IsNullOrEmpty(BaseUrl) ? $"/{BaseUrl}/search" : "/search";
|
||||
}
|
||||
|
||||
userLoginRepo.Insert(new UserLogins
|
||||
{
|
||||
LastLoggedIn = DateTime.UtcNow,
|
||||
Type = UserType.LocalUser,
|
||||
UserId = userId.ToString()
|
||||
});
|
||||
|
||||
return CustomModuleExtensions.LoginAndRedirect(this,userId.Value, expiry, redirect);
|
||||
};
|
||||
|
||||
Get["/register"] = x =>
|
||||
{
|
||||
{
|
||||
dynamic model = new ExpandoObject();
|
||||
model.Errored = Request.Query.error.HasValue;
|
||||
|
||||
return View["Register", model];
|
||||
}
|
||||
};
|
||||
|
||||
Post["/register"] = x =>
|
||||
{
|
||||
var username = (string)Request.Form.Username;
|
||||
var exists = UserMapper.DoUsersExist();
|
||||
if (exists)
|
||||
{
|
||||
return
|
||||
Context.GetRedirect(!string.IsNullOrEmpty(BaseUrl)
|
||||
? $"~/{BaseUrl}/register?error=true"
|
||||
: "~/register?error=true");
|
||||
}
|
||||
var userId = UserMapper.CreateUser(username, Request.Form.Password, EnumHelper<Permissions>.All(), 0);
|
||||
Session[SessionKeys.UsernameKey] = username;
|
||||
return CustomModuleExtensions.LoginAndRedirect(this, (Guid)userId);
|
||||
};
|
||||
|
||||
Get["/changepassword"] = _ => ChangePassword();
|
||||
Post["/changepassword"] = _ => ChangePasswordPost();
|
||||
}
|
||||
|
||||
private ICustomUserMapper UserMapper { get; }
|
||||
|
||||
private Negotiator ChangePassword()
|
||||
{
|
||||
this.RequiresAuthentication();
|
||||
return View["ChangePassword"];
|
||||
}
|
||||
|
||||
private Response ChangePasswordPost()
|
||||
{
|
||||
var username = Context.CurrentUser.UserName;
|
||||
var oldPass = Request.Form.OldPassword;
|
||||
var newPassword = Request.Form.NewPassword;
|
||||
var newPasswordAgain = Request.Form.NewPasswordAgain;
|
||||
|
||||
if (string.IsNullOrEmpty(oldPass) || string.IsNullOrEmpty(newPassword) ||
|
||||
string.IsNullOrEmpty(newPasswordAgain))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Message = "Please fill in all fields", Result = false });
|
||||
}
|
||||
|
||||
if (!newPassword.Equals(newPasswordAgain))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Message = "The passwords do not match", Result = false });
|
||||
}
|
||||
try
|
||||
{
|
||||
var result = UserMapper.UpdatePassword(username, oldPass, newPassword);
|
||||
if (result)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Message = "Password has been changed!", Result = true });
|
||||
}
|
||||
|
||||
return Response.AsJson(new JsonResponseModel { Message = "Could not update the password in the database", Result = false });
|
||||
}
|
||||
catch (SecurityException e)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Message = e.ToString(), Result = false });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
412
Ombi.UI/Modules/RequestsModule.cs
Normal file
412
Ombi.UI/Modules/RequestsModule.cs
Normal file
|
@ -0,0 +1,412 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: RequestsModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Nancy;
|
||||
using Nancy.Responses.Negotiation;
|
||||
using NLog;
|
||||
using Ombi.Api.Interfaces;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.Models;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Helpers.Analytics;
|
||||
using Ombi.Helpers.Permissions;
|
||||
using Ombi.Services.Interfaces;
|
||||
using Ombi.Services.Notification;
|
||||
using Ombi.Store;
|
||||
using Ombi.UI.Models;
|
||||
using Action = Ombi.Helpers.Analytics.Action;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class RequestsModule : BaseAuthModule
|
||||
{
|
||||
public RequestsModule(
|
||||
IRequestService service,
|
||||
ISettingsService<PlexRequestSettings> prSettings,
|
||||
ISettingsService<PlexSettings> plex,
|
||||
INotificationService notify,
|
||||
ISettingsService<SonarrSettings> sonarrSettings,
|
||||
ISettingsService<SickRageSettings> sickRageSettings,
|
||||
ISettingsService<CouchPotatoSettings> cpSettings,
|
||||
ICouchPotatoApi cpApi,
|
||||
ISonarrApi sonarrApi,
|
||||
ISickRageApi sickRageApi,
|
||||
ICacheProvider cache,
|
||||
IAnalytics an,
|
||||
INotificationEngine engine,
|
||||
ISecurityExtensions security) : base("requests", prSettings, security)
|
||||
{
|
||||
Service = service;
|
||||
PrSettings = prSettings;
|
||||
PlexSettings = plex;
|
||||
NotificationService = notify;
|
||||
SonarrSettings = sonarrSettings;
|
||||
SickRageSettings = sickRageSettings;
|
||||
CpSettings = cpSettings;
|
||||
SonarrApi = sonarrApi;
|
||||
SickRageApi = sickRageApi;
|
||||
CpApi = cpApi;
|
||||
Cache = cache;
|
||||
Analytics = an;
|
||||
NotificationEngine = engine;
|
||||
|
||||
Get["/", true] = async (x, ct) => await LoadRequests();
|
||||
Get["/movies", true] = async (x, ct) => await GetMovies();
|
||||
Get["/tvshows", true] = async (c, ct) => await GetTvShows();
|
||||
Get["/albums", true] = async (x, ct) => await GetAlbumRequests();
|
||||
Post["/delete", true] = async (x, ct) => await DeleteRequest((int)Request.Form.id);
|
||||
Post["/reportissue", true] = async (x, ct) => await ReportIssue((int)Request.Form.requestId, (IssueState)(int)Request.Form.issue, null);
|
||||
Post["/reportissuecomment", true] = async (x, ct) => await ReportIssue((int)Request.Form.requestId, IssueState.Other, (string)Request.Form.commentArea);
|
||||
|
||||
Post["/clearissues", true] = async (x, ct) => await ClearIssue((int)Request.Form.Id);
|
||||
|
||||
Post["/changeavailability", true] = async (x, ct) => await ChangeRequestAvailability((int)Request.Form.Id, (bool)Request.Form.Available);
|
||||
}
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
private IRequestService Service { get; }
|
||||
private IAnalytics Analytics { get; }
|
||||
private INotificationService NotificationService { get; }
|
||||
private ISettingsService<PlexRequestSettings> PrSettings { get; }
|
||||
private ISettingsService<PlexSettings> PlexSettings { get; }
|
||||
private ISettingsService<SonarrSettings> SonarrSettings { get; }
|
||||
private ISettingsService<SickRageSettings> SickRageSettings { get; }
|
||||
private ISettingsService<CouchPotatoSettings> CpSettings { get; }
|
||||
private ISonarrApi SonarrApi { get; }
|
||||
private ISickRageApi SickRageApi { get; }
|
||||
private ICouchPotatoApi CpApi { get; }
|
||||
private ICacheProvider Cache { get; }
|
||||
private INotificationEngine NotificationEngine { get; }
|
||||
|
||||
private async Task<Negotiator> LoadRequests()
|
||||
{
|
||||
var settings = await PrSettings.GetSettingsAsync();
|
||||
return View["Index", settings];
|
||||
}
|
||||
|
||||
private async Task<Response> GetMovies()
|
||||
{
|
||||
var settings = PrSettings.GetSettings();
|
||||
|
||||
var allRequests = await Service.GetAllAsync();
|
||||
allRequests = allRequests.Where(x => x.Type == RequestType.Movie);
|
||||
|
||||
var dbMovies = allRequests.ToList();
|
||||
|
||||
if (Security.HasPermissions(User, Permissions.UsersCanViewOnlyOwnRequests) && !IsAdmin)
|
||||
{
|
||||
dbMovies = dbMovies.Where(x => x.UserHasRequested(Username)).ToList();
|
||||
}
|
||||
|
||||
List<QualityModel> qualities = new List<QualityModel>();
|
||||
|
||||
if (IsAdmin)
|
||||
{
|
||||
var cpSettings = CpSettings.GetSettings();
|
||||
if (cpSettings.Enabled)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await Cache.GetOrSetAsync(CacheKeys.CouchPotatoQualityProfiles, async () =>
|
||||
{
|
||||
return await Task.Run(() => CpApi.GetProfiles(cpSettings.FullUri, cpSettings.ApiKey)).ConfigureAwait(false);
|
||||
});
|
||||
if (result != null)
|
||||
{
|
||||
qualities = result.list.Select(x => new QualityModel { Id = x._id, Name = x.label }).ToList();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Info(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var canManageRequest = Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ManageRequests);
|
||||
var viewModel = dbMovies.Select(movie => new RequestViewModel
|
||||
{
|
||||
ProviderId = movie.ProviderId,
|
||||
Type = movie.Type,
|
||||
Status = movie.Status,
|
||||
ImdbId = movie.ImdbId,
|
||||
Id = movie.Id,
|
||||
PosterPath = movie.PosterPath,
|
||||
ReleaseDate = movie.ReleaseDate,
|
||||
ReleaseDateTicks = movie.ReleaseDate.Ticks,
|
||||
RequestedDate = movie.RequestedDate,
|
||||
Released = DateTime.Now > movie.ReleaseDate,
|
||||
RequestedDateTicks = DateTimeHelper.OffsetUTCDateTime(movie.RequestedDate, DateTimeOffset).Ticks,
|
||||
Approved = movie.Available || movie.Approved,
|
||||
Title = movie.Title,
|
||||
Overview = movie.Overview,
|
||||
RequestedUsers = canManageRequest ? movie.AllUsers.ToArray() : new string[] { },
|
||||
ReleaseYear = movie.ReleaseDate.Year.ToString(),
|
||||
Available = movie.Available,
|
||||
Admin = canManageRequest,
|
||||
IssueId = movie.IssueId,
|
||||
Denied = movie.Denied,
|
||||
DeniedReason = movie.DeniedReason,
|
||||
Qualities = qualities.ToArray()
|
||||
}).ToList();
|
||||
|
||||
return Response.AsJson(viewModel);
|
||||
}
|
||||
|
||||
private async Task<Response> GetTvShows()
|
||||
{
|
||||
var settingsTask = PrSettings.GetSettingsAsync();
|
||||
|
||||
var requests = await Service.GetAllAsync();
|
||||
requests = requests.Where(x => x.Type == RequestType.TvShow);
|
||||
|
||||
var dbTv = requests;
|
||||
var settings = await settingsTask;
|
||||
if (Security.HasPermissions(User, Permissions.UsersCanViewOnlyOwnRequests) && !IsAdmin)
|
||||
{
|
||||
dbTv = dbTv.Where(x => x.UserHasRequested(Username)).ToList();
|
||||
}
|
||||
|
||||
IEnumerable<QualityModel> qualities = new List<QualityModel>();
|
||||
if (IsAdmin)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sonarrSettings = await SonarrSettings.GetSettingsAsync();
|
||||
if (sonarrSettings.Enabled)
|
||||
{
|
||||
var result = Cache.GetOrSetAsync(CacheKeys.SonarrQualityProfiles, async () =>
|
||||
{
|
||||
return await Task.Run(() => SonarrApi.GetProfiles(sonarrSettings.ApiKey, sonarrSettings.FullUri));
|
||||
});
|
||||
qualities = result.Result.Select(x => new QualityModel { Id = x.id.ToString(), Name = x.name }).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
var sickRageSettings = await SickRageSettings.GetSettingsAsync();
|
||||
if (sickRageSettings.Enabled)
|
||||
{
|
||||
qualities = sickRageSettings.Qualities.Select(x => new QualityModel { Id = x.Key, Name = x.Value }).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Info(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var canManageRequest = Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ManageRequests);
|
||||
var viewModel = dbTv.Select(tv => new RequestViewModel
|
||||
{
|
||||
ProviderId = tv.ProviderId,
|
||||
Type = tv.Type,
|
||||
Status = tv.Status,
|
||||
ImdbId = tv.ImdbId,
|
||||
Id = tv.Id,
|
||||
PosterPath = tv.PosterPath,
|
||||
ReleaseDate = tv.ReleaseDate,
|
||||
ReleaseDateTicks = tv.ReleaseDate.Ticks,
|
||||
RequestedDate = tv.RequestedDate,
|
||||
RequestedDateTicks = DateTimeHelper.OffsetUTCDateTime(tv.RequestedDate, DateTimeOffset).Ticks,
|
||||
Released = DateTime.Now > tv.ReleaseDate,
|
||||
Approved = tv.Available || tv.Approved,
|
||||
Title = tv.Title,
|
||||
Overview = tv.Overview,
|
||||
RequestedUsers = canManageRequest ? tv.AllUsers.ToArray() : new string[] { },
|
||||
ReleaseYear = tv.ReleaseDate.Year.ToString(),
|
||||
Available = tv.Available,
|
||||
Admin = canManageRequest,
|
||||
IssueId = tv.IssueId,
|
||||
Denied = tv.Denied,
|
||||
DeniedReason = tv.DeniedReason,
|
||||
TvSeriesRequestType = tv.SeasonsRequested,
|
||||
Qualities = qualities.ToArray(),
|
||||
Episodes = tv.Episodes.ToArray(),
|
||||
}).ToList();
|
||||
|
||||
return Response.AsJson(viewModel);
|
||||
}
|
||||
|
||||
private async Task<Response> GetAlbumRequests()
|
||||
{
|
||||
var settings = PrSettings.GetSettings();
|
||||
var dbAlbum = await Service.GetAllAsync();
|
||||
dbAlbum = dbAlbum.Where(x => x.Type == RequestType.Album);
|
||||
if (Security.HasPermissions(User, Permissions.UsersCanViewOnlyOwnRequests) && !IsAdmin)
|
||||
{
|
||||
dbAlbum = dbAlbum.Where(x => x.UserHasRequested(Username));
|
||||
}
|
||||
var canManageRequest = Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ManageRequests);
|
||||
var viewModel = dbAlbum.Select(album =>
|
||||
{
|
||||
return new RequestViewModel
|
||||
{
|
||||
ProviderId = album.ProviderId,
|
||||
Type = album.Type,
|
||||
Status = album.Status,
|
||||
ImdbId = album.ImdbId,
|
||||
Id = album.Id,
|
||||
PosterPath = album.PosterPath,
|
||||
ReleaseDate = album.ReleaseDate,
|
||||
ReleaseDateTicks = album.ReleaseDate.Ticks,
|
||||
RequestedDate = album.RequestedDate,
|
||||
RequestedDateTicks = DateTimeHelper.OffsetUTCDateTime(album.RequestedDate, DateTimeOffset).Ticks,
|
||||
Released = DateTime.Now > album.ReleaseDate,
|
||||
Approved = album.Available || album.Approved,
|
||||
Title = album.Title,
|
||||
Overview = album.Overview,
|
||||
RequestedUsers = canManageRequest ? album.AllUsers.ToArray() : new string[] { },
|
||||
ReleaseYear = album.ReleaseDate.Year.ToString(),
|
||||
Available = album.Available,
|
||||
Admin = canManageRequest,
|
||||
IssueId = album.IssueId,
|
||||
Denied = album.Denied,
|
||||
DeniedReason = album.DeniedReason,
|
||||
TvSeriesRequestType = album.SeasonsRequested,
|
||||
MusicBrainzId = album.MusicBrainzId,
|
||||
ArtistName = album.ArtistName
|
||||
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
return Response.AsJson(viewModel);
|
||||
}
|
||||
|
||||
private async Task<Response> DeleteRequest(int requestid)
|
||||
{
|
||||
if (!Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ManageRequests))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = true });
|
||||
}
|
||||
|
||||
|
||||
Analytics.TrackEventAsync(Category.Requests, Action.Delete, "Delete Request", Username, CookieHelper.GetAnalyticClientId(Cookies));
|
||||
|
||||
var currentEntity = await Service.GetAsync(requestid);
|
||||
await Service.DeleteRequestAsync(currentEntity);
|
||||
return Response.AsJson(new JsonResponseModel { Result = true });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reports the issue.
|
||||
/// Comment can be null if the <c>IssueState != Other</c>
|
||||
/// </summary>
|
||||
/// <param name="requestId">The request identifier.</param>
|
||||
/// <param name="issue">The issue.</param>
|
||||
/// <param name="comment">The comment.</param>
|
||||
/// <returns></returns>
|
||||
private async Task<Response> ReportIssue(int requestId, IssueState issue, string comment)
|
||||
{
|
||||
if (!Security.HasPermissions(User, Permissions.ReportIssue))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Sorry, you do not have the correct permissions to report an issue." });
|
||||
}
|
||||
var originalRequest = await Service.GetAsync(requestId);
|
||||
if (originalRequest == null)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not add issue, please try again or contact the administrator!" });
|
||||
}
|
||||
originalRequest.Issues = issue;
|
||||
originalRequest.OtherMessage = !string.IsNullOrEmpty(comment)
|
||||
? $"{Username} - {comment}"
|
||||
: string.Empty;
|
||||
|
||||
|
||||
var result = await Service.UpdateRequestAsync(originalRequest);
|
||||
|
||||
var model = new NotificationModel
|
||||
{
|
||||
User = Username,
|
||||
NotificationType = NotificationType.Issue,
|
||||
Title = originalRequest.Title,
|
||||
DateTime = DateTime.Now,
|
||||
Body = issue == IssueState.Other ? comment : issue.ToString().ToCamelCaseWords()
|
||||
};
|
||||
await NotificationService.Publish(model);
|
||||
|
||||
return Response.AsJson(result
|
||||
? new JsonResponseModel { Result = true }
|
||||
: new JsonResponseModel { Result = false, Message = "Could not add issue, please try again or contact the administrator!" });
|
||||
}
|
||||
|
||||
private async Task<Response> ClearIssue(int requestId)
|
||||
{
|
||||
if (!Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ManageRequests))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Sorry, you do not have the correct permissions to clear an issue." });
|
||||
}
|
||||
|
||||
var originalRequest = await Service.GetAsync(requestId);
|
||||
if (originalRequest == null)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Request does not exist to clear it!" });
|
||||
}
|
||||
originalRequest.Issues = IssueState.None;
|
||||
originalRequest.OtherMessage = string.Empty;
|
||||
|
||||
var result = await Service.UpdateRequestAsync(originalRequest);
|
||||
return Response.AsJson(result
|
||||
? new JsonResponseModel { Result = true }
|
||||
: new JsonResponseModel { Result = false, Message = "Could not clear issue, please try again or check the logs" });
|
||||
}
|
||||
|
||||
private async Task<Response> ChangeRequestAvailability(int requestId, bool available)
|
||||
{
|
||||
if (!Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ManageRequests))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Sorry, you do not have the correct permissions to change a request." });
|
||||
}
|
||||
|
||||
Analytics.TrackEventAsync(Category.Requests, Action.Update, available ? "Make request available" : "Make request unavailable", Username, CookieHelper.GetAnalyticClientId(Cookies));
|
||||
var originalRequest = await Service.GetAsync(requestId);
|
||||
if (originalRequest == null)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Request does not exist to change the availability!" });
|
||||
}
|
||||
|
||||
originalRequest.Available = available;
|
||||
|
||||
var result = await Service.UpdateRequestAsync(originalRequest);
|
||||
var plexService = await PlexSettings.GetSettingsAsync();
|
||||
await NotificationEngine.NotifyUsers(originalRequest, plexService.PlexAuthToken, available ? NotificationType.RequestAvailable : NotificationType.RequestDeclined);
|
||||
return Response.AsJson(result
|
||||
? new { Result = true, Available = available, Message = string.Empty }
|
||||
: new { Result = false, Available = false, Message = "Could not update the availability, please try again or check the logs" });
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
1388
Ombi.UI/Modules/SearchModule.cs
Normal file
1388
Ombi.UI/Modules/SearchModule.cs
Normal file
File diff suppressed because it is too large
Load diff
369
Ombi.UI/Modules/UserLoginModule.cs
Normal file
369
Ombi.UI/Modules/UserLoginModule.cs
Normal file
|
@ -0,0 +1,369 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: UserLoginModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Nancy;
|
||||
using Nancy.Extensions;
|
||||
using Nancy.Linker;
|
||||
using NLog;
|
||||
using Ombi.Api.Interfaces;
|
||||
using Ombi.Api.Models.Plex;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Core.Users;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Helpers.Analytics;
|
||||
using Ombi.Helpers.Permissions;
|
||||
using Ombi.Store;
|
||||
using Ombi.Store.Models;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.UI.Authentication;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class UserLoginModule : BaseModule
|
||||
{
|
||||
public UserLoginModule(ISettingsService<AuthenticationSettings> auth, IPlexApi api, ISettingsService<PlexSettings> plexSettings, ISettingsService<PlexRequestSettings> pr,
|
||||
ISettingsService<LandingPageSettings> lp, IAnalytics a, IResourceLinker linker, IRepository<UserLogins> userLogins, IPlexUserRepository plexUsers, ICustomUserMapper custom,
|
||||
ISecurityExtensions security, ISettingsService<UserManagementSettings> userManagementSettings)
|
||||
: base("userlogin", pr, security)
|
||||
{
|
||||
AuthService = auth;
|
||||
LandingPageSettings = lp;
|
||||
Analytics = a;
|
||||
Api = api;
|
||||
PlexSettings = plexSettings;
|
||||
Linker = linker;
|
||||
UserLogins = userLogins;
|
||||
PlexUserRepository = plexUsers;
|
||||
CustomUserMapper = custom;
|
||||
UserManagementSettings = userManagementSettings;
|
||||
|
||||
Get["UserLoginIndex", "/", true] = async (x, ct) =>
|
||||
{
|
||||
if (Request.Query["landing"] == null)
|
||||
{
|
||||
var s = await LandingPageSettings.GetSettingsAsync();
|
||||
if (s.Enabled)
|
||||
{
|
||||
if (s.BeforeLogin) // Before login
|
||||
{
|
||||
if (string.IsNullOrEmpty(Username))
|
||||
{
|
||||
// They are not logged in
|
||||
return
|
||||
Context.GetRedirect(Linker.BuildRelativeUri(Context, "LandingPageIndex").ToString());
|
||||
}
|
||||
return Context.GetRedirect(Linker.BuildRelativeUri(Context, "SearchIndex").ToString());
|
||||
}
|
||||
|
||||
// After login
|
||||
if (string.IsNullOrEmpty(Username))
|
||||
{
|
||||
// Not logged in yet
|
||||
return Context.GetRedirect(Linker.BuildRelativeUri(Context, "UserLoginIndex").ToString() + "?landing");
|
||||
}
|
||||
// Send them to landing
|
||||
var landingUrl = Linker.BuildRelativeUri(Context, "LandingPageIndex").ToString();
|
||||
return Context.GetRedirect(landingUrl);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(Username) || IsAdmin)
|
||||
{
|
||||
var url = Linker.BuildRelativeUri(Context, "SearchIndex").ToString();
|
||||
return Response.AsRedirect(url);
|
||||
}
|
||||
var settings = await AuthService.GetSettingsAsync();
|
||||
return View["Index", settings];
|
||||
};
|
||||
|
||||
Post["/", true] = async (x, ct) => await LoginUser();
|
||||
Get["/logout"] = x => Logout();
|
||||
}
|
||||
|
||||
private ISettingsService<AuthenticationSettings> AuthService { get; }
|
||||
private ISettingsService<LandingPageSettings> LandingPageSettings { get; }
|
||||
private ISettingsService<PlexSettings> PlexSettings { get; }
|
||||
private IPlexApi Api { get; }
|
||||
private IResourceLinker Linker { get; }
|
||||
private IAnalytics Analytics { get; }
|
||||
private IRepository<UserLogins> UserLogins { get; }
|
||||
private IPlexUserRepository PlexUserRepository { get; }
|
||||
private ICustomUserMapper CustomUserMapper { get; }
|
||||
private ISettingsService<UserManagementSettings> UserManagementSettings {get;}
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private async Task<Response> LoginUser()
|
||||
{
|
||||
var userId = string.Empty;
|
||||
var loginGuid = Guid.Empty;
|
||||
var dateTimeOffset = Request.Form.DateTimeOffset;
|
||||
var username = Request.Form.username.Value;
|
||||
Log.Debug("Username \"{0}\" attempting to login", username);
|
||||
if (string.IsNullOrWhiteSpace(username))
|
||||
{
|
||||
Session["TempMessage"] = Resources.UI.UserLogin_IncorrectUserPass;
|
||||
var uri = Linker.BuildRelativeUri(Context, "UserLoginIndex");
|
||||
return Response.AsRedirect(uri.ToString());
|
||||
}
|
||||
|
||||
var authenticated = false;
|
||||
var isOwner = false;
|
||||
|
||||
var settings = await AuthService.GetSettingsAsync();
|
||||
var plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
|
||||
if (IsUserInDeniedList(username, settings))
|
||||
{
|
||||
Log.Debug("User is in denied list, not allowing them to authenticate");
|
||||
Session["TempMessage"] = Resources.UI.UserLogin_IncorrectUserPass;
|
||||
var uri = Linker.BuildRelativeUri(Context, "UserLoginIndex");
|
||||
return Response.AsRedirect(uri.ToString());
|
||||
}
|
||||
|
||||
var password = string.Empty;
|
||||
if (settings.UsePassword)
|
||||
{
|
||||
Log.Debug("Using password");
|
||||
password = Request.Form.password.Value;
|
||||
}
|
||||
|
||||
var localUsers = await CustomUserMapper.GetUsersAsync();
|
||||
var plexLocalUsers = await PlexUserRepository.GetAllAsync();
|
||||
|
||||
|
||||
if (settings.UserAuthentication && settings.UsePassword) // Authenticate with Plex
|
||||
{
|
||||
Log.Debug("Need to auth and also provide pass");
|
||||
var signedIn = (PlexAuthentication)Api.SignIn(username, password);
|
||||
if (signedIn.user?.authentication_token != null)
|
||||
{
|
||||
Log.Debug("Correct credentials, checking if the user is account owner or in the friends list");
|
||||
if (CheckIfUserIsOwner(plexSettings.PlexAuthToken, signedIn.user?.username))
|
||||
{
|
||||
Log.Debug("User is the account owner");
|
||||
authenticated = true;
|
||||
isOwner = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
authenticated = CheckIfUserIsInPlexFriends(username, plexSettings.PlexAuthToken);
|
||||
Log.Debug("Friends list result = {0}", authenticated);
|
||||
}
|
||||
userId = signedIn.user.uuid;
|
||||
}
|
||||
}
|
||||
else if (settings.UserAuthentication) // Check against the users in Plex
|
||||
{
|
||||
Log.Debug("Need to auth");
|
||||
authenticated = CheckIfUserIsInPlexFriends(username, plexSettings.PlexAuthToken);
|
||||
if (authenticated)
|
||||
{
|
||||
userId = GetUserIdIsInPlexFriends(username, plexSettings.PlexAuthToken);
|
||||
}
|
||||
if (CheckIfUserIsOwner(plexSettings.PlexAuthToken, username))
|
||||
{
|
||||
Log.Debug("User is the account owner");
|
||||
authenticated = true;
|
||||
isOwner = true;
|
||||
userId = GetOwnerId(plexSettings.PlexAuthToken, username);
|
||||
}
|
||||
Log.Debug("Friends list result = {0}", authenticated);
|
||||
}
|
||||
else if (!settings.UserAuthentication) // No auth, let them pass!
|
||||
{
|
||||
Log.Debug("No need to auth");
|
||||
authenticated = true;
|
||||
}
|
||||
|
||||
if (authenticated)
|
||||
{
|
||||
UserLogins.Insert(new UserLogins { UserId = userId, Type = UserType.PlexUser, LastLoggedIn = DateTime.UtcNow });
|
||||
Log.Debug("We are authenticated! Setting session.");
|
||||
// Add to the session (Used in the BaseModules)
|
||||
Session[SessionKeys.UsernameKey] = (string)username;
|
||||
Session[SessionKeys.ClientDateTimeOffsetKey] = (int)dateTimeOffset;
|
||||
|
||||
var plexLocal = plexLocalUsers.FirstOrDefault(x => x.Username == username);
|
||||
if (plexLocal != null)
|
||||
{
|
||||
loginGuid = Guid.Parse(plexLocal.LoginId);
|
||||
}
|
||||
|
||||
var dbUser = localUsers.FirstOrDefault(x => x.UserName == username);
|
||||
if (dbUser != null)
|
||||
{
|
||||
loginGuid = Guid.Parse(dbUser.UserGuid);
|
||||
}
|
||||
|
||||
if (loginGuid != Guid.Empty)
|
||||
{
|
||||
if (!settings.UserAuthentication)// Do not need to auth make admin use login screen for now TODO remove this
|
||||
{
|
||||
if (dbUser != null)
|
||||
{
|
||||
var perms = (Permissions) dbUser.Permissions;
|
||||
if (perms.HasFlag(Permissions.Administrator))
|
||||
{
|
||||
var uri = Linker.BuildRelativeUri(Context, "UserLoginIndex");
|
||||
Session["TempMessage"] = Resources.UI.UserLogin_AdminUsePassword;
|
||||
return Response.AsRedirect(uri.ToString());
|
||||
}
|
||||
}
|
||||
if (plexLocal != null)
|
||||
{
|
||||
var perms = (Permissions)plexLocal.Permissions;
|
||||
if (perms.HasFlag(Permissions.Administrator))
|
||||
{
|
||||
var uri = Linker.BuildRelativeUri(Context, "UserLoginIndex");
|
||||
Session["TempMessage"] = Resources.UI.UserLogin_AdminUsePassword;
|
||||
return Response.AsRedirect(uri.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(loginGuid == Guid.Empty && settings.UserAuthentication)
|
||||
{
|
||||
var defaultSettings = UserManagementSettings.GetSettings();
|
||||
loginGuid = Guid.NewGuid();
|
||||
|
||||
var defaultPermissions = (Permissions)UserManagementHelper.GetPermissions(defaultSettings);
|
||||
if (isOwner)
|
||||
{
|
||||
// If we are the owner, add the admin permission.
|
||||
if (!defaultPermissions.HasFlag(Permissions.Administrator))
|
||||
{
|
||||
defaultPermissions += (int)Permissions.Administrator;
|
||||
}
|
||||
}
|
||||
|
||||
// Looks like we still don't have an entry, so this user does not exist
|
||||
await PlexUserRepository.InsertAsync(new PlexUsers
|
||||
{
|
||||
PlexUserId = userId,
|
||||
UserAlias = string.Empty,
|
||||
Permissions = (int)defaultPermissions,
|
||||
Features = UserManagementHelper.GetPermissions(defaultSettings),
|
||||
Username = username,
|
||||
EmailAddress = string.Empty, // We don't have it, we will get it on the next scheduled job run (in 30 mins)
|
||||
LoginId = loginGuid.ToString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!authenticated)
|
||||
{
|
||||
var uri = Linker.BuildRelativeUri(Context, "UserLoginIndex");
|
||||
Session["TempMessage"] = Resources.UI.UserLogin_IncorrectUserPass;
|
||||
return Response.AsRedirect(uri.ToString());
|
||||
}
|
||||
|
||||
var landingSettings = await LandingPageSettings.GetSettingsAsync();
|
||||
|
||||
if (landingSettings.Enabled)
|
||||
{
|
||||
if (!landingSettings.BeforeLogin)
|
||||
{
|
||||
var uri = Linker.BuildRelativeUri(Context, "LandingPageIndex");
|
||||
if (loginGuid != Guid.Empty)
|
||||
{
|
||||
return CustomModuleExtensions.LoginAndRedirect(this, loginGuid, null, uri.ToString());
|
||||
}
|
||||
return Response.AsRedirect(uri.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var retVal = Linker.BuildRelativeUri(Context, "SearchIndex");
|
||||
if (loginGuid != Guid.Empty)
|
||||
{
|
||||
return CustomModuleExtensions.LoginAndRedirect(this, loginGuid, null, retVal.ToString());
|
||||
}
|
||||
return Response.AsRedirect(retVal.ToString());
|
||||
}
|
||||
|
||||
|
||||
|
||||
private Response Logout()
|
||||
{
|
||||
if (Session[SessionKeys.UsernameKey] != null)
|
||||
{
|
||||
Session.Delete(SessionKeys.UsernameKey);
|
||||
}
|
||||
return Context.GetRedirect(!string.IsNullOrEmpty(BaseUrl)
|
||||
? $"~/{BaseUrl}/userlogin"
|
||||
: "~/userlogin");
|
||||
}
|
||||
|
||||
private bool CheckIfUserIsOwner(string authToken, string userName)
|
||||
{
|
||||
var userAccount = Api.GetAccount(authToken);
|
||||
if (userAccount == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return userAccount.Username != null && userAccount.Username.Equals(userName, StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
|
||||
private string GetOwnerId(string authToken, string userName)
|
||||
{
|
||||
var userAccount = Api.GetAccount(authToken);
|
||||
if (userAccount == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
return userAccount.Id;
|
||||
}
|
||||
|
||||
private bool CheckIfUserIsInPlexFriends(string username, string authToken)
|
||||
{
|
||||
var users = Api.GetUsers(authToken);
|
||||
var allUsers = users?.User?.Where(x => !string.IsNullOrEmpty(x.Title));
|
||||
return allUsers != null && allUsers.Any(x => x.Title.Equals(username, StringComparison.CurrentCultureIgnoreCase));
|
||||
}
|
||||
|
||||
|
||||
private string GetUserIdIsInPlexFriends(string username, string authToken)
|
||||
{
|
||||
var users = Api.GetUsers(authToken);
|
||||
var allUsers = users?.User?.Where(x => !string.IsNullOrEmpty(x.Title));
|
||||
return allUsers?.Where(x => x.Title.Equals(username, StringComparison.CurrentCultureIgnoreCase)).Select(x => x.Id).FirstOrDefault();
|
||||
}
|
||||
|
||||
private bool IsUserInDeniedList(string username, AuthenticationSettings settings)
|
||||
{
|
||||
return settings.DeniedUserList.Any(x => x.Equals(username, StringComparison.CurrentCultureIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
507
Ombi.UI/Modules/UserManagementModule.cs
Normal file
507
Ombi.UI/Modules/UserManagementModule.cs
Normal file
|
@ -0,0 +1,507 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Nancy;
|
||||
using Nancy.Extensions;
|
||||
using Nancy.Responses.Negotiation;
|
||||
using Newtonsoft.Json;
|
||||
using Ombi.Api.Interfaces;
|
||||
using Ombi.Api.Models.Plex;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.Models;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Helpers.Analytics;
|
||||
using Ombi.Helpers.Permissions;
|
||||
using Ombi.Store;
|
||||
using Ombi.Store.Models;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.UI.Models;
|
||||
using Ombi.UI.Models.UserManagement;
|
||||
using Action = Ombi.Helpers.Analytics.Action;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class UserManagementModule : BaseModule
|
||||
{
|
||||
public UserManagementModule(ISettingsService<PlexRequestSettings> pr, ICustomUserMapper m, IPlexApi plexApi, ISettingsService<PlexSettings> plex, IRepository<UserLogins> userLogins, IPlexUserRepository plexRepo
|
||||
, ISecurityExtensions security, IRequestService req, IAnalytics ana) : base("usermanagement", pr, security)
|
||||
{
|
||||
#if !DEBUG
|
||||
Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
|
||||
#endif
|
||||
UserMapper = m;
|
||||
PlexApi = plexApi;
|
||||
PlexSettings = plex;
|
||||
UserLoginsRepo = userLogins;
|
||||
PlexUsersRepository = plexRepo;
|
||||
PlexRequestSettings = pr;
|
||||
RequestService = req;
|
||||
Analytics = ana;
|
||||
|
||||
Get["/"] = x => Load();
|
||||
|
||||
Get["/users", true] = async (x, ct) => await LoadUsers();
|
||||
Post["/createuser", true] = async (x, ct) => await CreateUser();
|
||||
Get["/local/{id}"] = x => LocalDetails((Guid)x.id);
|
||||
Get["/plex/{id}", true] = async (x, ct) => await PlexDetails(x.id);
|
||||
Get["/permissions"] = x => GetEnum<Permissions>();
|
||||
Get["/features"] = x => GetEnum<Features>();
|
||||
Post["/updateuser", true] = async (x, ct) => await UpdateUser();
|
||||
Post["/deleteuser"] = x => DeleteUser();
|
||||
}
|
||||
|
||||
private ICustomUserMapper UserMapper { get; }
|
||||
private IPlexApi PlexApi { get; }
|
||||
private ISettingsService<PlexSettings> PlexSettings { get; }
|
||||
private IRepository<UserLogins> UserLoginsRepo { get; }
|
||||
private IPlexUserRepository PlexUsersRepository { get; }
|
||||
private ISettingsService<PlexRequestSettings> PlexRequestSettings { get; }
|
||||
private IRequestService RequestService { get; }
|
||||
private IAnalytics Analytics { get; }
|
||||
|
||||
private Negotiator Load()
|
||||
{
|
||||
return View["Index"];
|
||||
}
|
||||
|
||||
private async Task<Response> LoadUsers()
|
||||
{
|
||||
var localUsers = await UserMapper.GetUsersAsync();
|
||||
var plexDbUsers = await PlexUsersRepository.GetAllAsync();
|
||||
var model = new List<UserManagementUsersViewModel>();
|
||||
|
||||
var userLogins = UserLoginsRepo.GetAll().ToList();
|
||||
|
||||
foreach (var user in localUsers)
|
||||
{
|
||||
var userDb = userLogins.FirstOrDefault(x => x.UserId == user.UserGuid);
|
||||
model.Add(MapLocalUser(user, userDb?.LastLoggedIn ?? DateTime.MinValue));
|
||||
}
|
||||
|
||||
var plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
if (!string.IsNullOrEmpty(plexSettings.PlexAuthToken))
|
||||
{
|
||||
//Get Plex Users
|
||||
var plexUsers = PlexApi.GetUsers(plexSettings.PlexAuthToken);
|
||||
|
||||
foreach (var u in plexUsers.User)
|
||||
{
|
||||
var dbUser = plexDbUsers.FirstOrDefault(x => x.PlexUserId == u.Id);
|
||||
var userDb = userLogins.FirstOrDefault(x => x.UserId == u.Id);
|
||||
|
||||
// We don't have the user in the database yet
|
||||
if (dbUser == null)
|
||||
{
|
||||
model.Add(MapPlexUser(u, null, userDb?.LastLoggedIn ?? DateTime.MinValue));
|
||||
}
|
||||
else
|
||||
{
|
||||
// The Plex User is in the database
|
||||
model.Add(MapPlexUser(u, dbUser, userDb?.LastLoggedIn ?? DateTime.MinValue));
|
||||
}
|
||||
}
|
||||
|
||||
// Also get the server admin
|
||||
var account = PlexApi.GetAccount(plexSettings.PlexAuthToken);
|
||||
if (account != null)
|
||||
{
|
||||
var dbUser = plexDbUsers.FirstOrDefault(x => x.PlexUserId == account.Id);
|
||||
var userDb = userLogins.FirstOrDefault(x => x.UserId == account.Id);
|
||||
model.Add(MapPlexAdmin(account, dbUser, userDb?.LastLoggedIn ?? DateTime.MinValue));
|
||||
}
|
||||
}
|
||||
return Response.AsJson(model);
|
||||
}
|
||||
|
||||
private async Task<Response> CreateUser()
|
||||
{
|
||||
Analytics.TrackEventAsync(Category.UserManagement, Action.Create, "Created User", Username, CookieHelper.GetAnalyticClientId(Cookies));
|
||||
var body = Request.Body.AsString();
|
||||
if (string.IsNullOrEmpty(body))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not save user, invalid JSON body" });
|
||||
}
|
||||
|
||||
var model = JsonConvert.DeserializeObject<UserManagementCreateModel>(body);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(model.Username) || string.IsNullOrWhiteSpace(model.Password))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = "Please enter in a valid Username and Password"
|
||||
});
|
||||
}
|
||||
|
||||
var users = await UserMapper.GetUsersAsync();
|
||||
if (users.Any(x => x.UserName.Equals(model.Username, StringComparison.CurrentCultureIgnoreCase)))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = $"A user with the username '{model.Username}' already exists"
|
||||
});
|
||||
}
|
||||
|
||||
var featuresVal = 0;
|
||||
var permissionsVal = 0;
|
||||
|
||||
foreach (var feature in model.Features)
|
||||
{
|
||||
var f = (int)EnumHelper<Features>.GetValueFromName(feature);
|
||||
featuresVal += f;
|
||||
}
|
||||
|
||||
foreach (var permission in model.Permissions)
|
||||
{
|
||||
var f = (int)EnumHelper<Permissions>.GetValueFromName(permission);
|
||||
permissionsVal += f;
|
||||
}
|
||||
|
||||
var user = UserMapper.CreateUser(model.Username, model.Password, permissionsVal, featuresVal, new UserProperties { EmailAddress = model.EmailAddress });
|
||||
if (user.HasValue)
|
||||
{
|
||||
return Response.AsJson(MapLocalUser(UserMapper.GetUser(user.Value), DateTime.MinValue));
|
||||
}
|
||||
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not save user" });
|
||||
}
|
||||
|
||||
private async Task<Response> UpdateUser()
|
||||
{
|
||||
Analytics.TrackEventAsync(Category.UserManagement, Action.Update, "Updated User", Username, CookieHelper.GetAnalyticClientId(Cookies));
|
||||
var body = Request.Body.AsString();
|
||||
if (string.IsNullOrEmpty(body))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not save user, invalid JSON body" });
|
||||
}
|
||||
|
||||
var model = JsonConvert.DeserializeObject<UserManagementUpdateModel>(body);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(model.Id))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = true,
|
||||
Message = "Couldn't find the user"
|
||||
});
|
||||
}
|
||||
|
||||
var permissionsValue = model.Permissions.Where(c => c.Selected).Sum(c => c.Value);
|
||||
var featuresValue = model.Features.Where(c => c.Selected).Sum(c => c.Value);
|
||||
|
||||
Guid outId;
|
||||
Guid.TryParse(model.Id, out outId);
|
||||
var localUser = UserMapper.GetUser(outId);
|
||||
|
||||
// Update Local User
|
||||
if (localUser != null)
|
||||
{
|
||||
localUser.Permissions = permissionsValue;
|
||||
localUser.Features = featuresValue;
|
||||
|
||||
var currentProps = ByteConverterHelper.ReturnObject<UserProperties>(localUser.UserProperties);
|
||||
|
||||
// Let's check if the alias has changed, if so we need to change all the requests associated with this
|
||||
await UpdateRequests(localUser.UserName, currentProps.UserAlias, model.Alias);
|
||||
|
||||
currentProps.UserAlias = model.Alias;
|
||||
currentProps.EmailAddress = model.EmailAddress;
|
||||
|
||||
localUser.UserProperties = ByteConverterHelper.ReturnBytes(currentProps);
|
||||
|
||||
var user = UserMapper.EditUser(localUser);
|
||||
var dbUser = UserLoginsRepo.GetAll().FirstOrDefault(x => x.UserId == user.UserGuid);
|
||||
var retUser = MapLocalUser(user, dbUser?.LastLoggedIn ?? DateTime.MinValue);
|
||||
return Response.AsJson(retUser);
|
||||
}
|
||||
|
||||
var plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
var plexDbUsers = await PlexUsersRepository.GetAllAsync();
|
||||
var plexUsers = PlexApi.GetUsers(plexSettings.PlexAuthToken);
|
||||
var plexDbUser = plexDbUsers.FirstOrDefault(x => x.PlexUserId == model.Id);
|
||||
var plexUser = plexUsers.User.FirstOrDefault(x => x.Id == model.Id);
|
||||
var userLogin = UserLoginsRepo.GetAll().FirstOrDefault(x => x.UserId == model.Id);
|
||||
if (plexDbUser != null && plexUser != null)
|
||||
{
|
||||
// We have a user in the DB for this Plex Account
|
||||
plexDbUser.Permissions = permissionsValue;
|
||||
plexDbUser.Features = featuresValue;
|
||||
|
||||
await UpdateRequests(plexDbUser.Username, plexDbUser.UserAlias, model.Alias);
|
||||
|
||||
plexDbUser.UserAlias = model.Alias;
|
||||
|
||||
await PlexUsersRepository.UpdateAsync(plexDbUser);
|
||||
|
||||
var retUser = MapPlexUser(plexUser, plexDbUser, userLogin?.LastLoggedIn ?? DateTime.MinValue);
|
||||
return Response.AsJson(retUser);
|
||||
}
|
||||
|
||||
// So it could actually be the admin
|
||||
var account = PlexApi.GetAccount(plexSettings.PlexAuthToken);
|
||||
if (plexDbUser != null && account != null)
|
||||
{
|
||||
// We have a user in the DB for this Plex Account
|
||||
plexDbUser.Permissions = permissionsValue;
|
||||
plexDbUser.Features = featuresValue;
|
||||
|
||||
await UpdateRequests(plexDbUser.Username, plexDbUser.UserAlias, model.Alias);
|
||||
|
||||
plexDbUser.UserAlias = model.Alias;
|
||||
|
||||
await PlexUsersRepository.UpdateAsync(plexDbUser);
|
||||
|
||||
var retUser = MapPlexAdmin(account, plexDbUser, userLogin?.LastLoggedIn ?? DateTime.MinValue);
|
||||
return Response.AsJson(retUser);
|
||||
}
|
||||
|
||||
// We have a Plex Account but he's not in the DB
|
||||
if (plexUser != null)
|
||||
{
|
||||
var user = new PlexUsers
|
||||
{
|
||||
Permissions = permissionsValue,
|
||||
Features = featuresValue,
|
||||
UserAlias = model.Alias,
|
||||
PlexUserId = plexUser.Id,
|
||||
EmailAddress = plexUser.Email,
|
||||
Username = plexUser.Username,
|
||||
LoginId = Guid.NewGuid().ToString()
|
||||
};
|
||||
|
||||
await PlexUsersRepository.InsertAsync(user);
|
||||
|
||||
var retUser = MapPlexUser(plexUser, user, userLogin?.LastLoggedIn ?? DateTime.MinValue);
|
||||
return Response.AsJson(retUser);
|
||||
}
|
||||
return null; // We should never end up here.
|
||||
}
|
||||
|
||||
private async Task UpdateRequests(string username, string oldAlias, string newAlias)
|
||||
{
|
||||
var newUsername = string.IsNullOrEmpty(newAlias) ? username : newAlias; // User the username if we are clearing the alias
|
||||
var olderUsername = string.IsNullOrEmpty(oldAlias) ? username : oldAlias;
|
||||
// Let's check if the alias has changed, if so we need to change all the requests associated with this
|
||||
if (!olderUsername.Equals(newUsername, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
var requests = await RequestService.GetAllAsync();
|
||||
// Update all requests
|
||||
var requestsWithThisUser = requests.Where(x => x.RequestedUsers.Contains(olderUsername)).ToList();
|
||||
foreach (var r in requestsWithThisUser)
|
||||
{
|
||||
r.RequestedUsers.Remove(olderUsername); // Remove old
|
||||
r.RequestedUsers.Add(newUsername); // Add new
|
||||
}
|
||||
|
||||
if (requestsWithThisUser.Any())
|
||||
{
|
||||
RequestService.BatchUpdate(requestsWithThisUser);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private Response DeleteUser()
|
||||
{
|
||||
Analytics.TrackEventAsync(Category.UserManagement, Action.Delete, "Deleted User", Username, CookieHelper.GetAnalyticClientId(Cookies));
|
||||
var body = Request.Body.AsString();
|
||||
if (string.IsNullOrEmpty(body))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not save user, invalid JSON body" });
|
||||
}
|
||||
|
||||
var model = JsonConvert.DeserializeObject<DeleteUserViewModel>(body);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(model.Id))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = true,
|
||||
Message = "Couldn't find the user"
|
||||
});
|
||||
}
|
||||
|
||||
UserMapper.DeleteUser(model.Id);
|
||||
|
||||
return Response.AsJson(new JsonResponseModel { Result = true });
|
||||
}
|
||||
|
||||
private Response LocalDetails(Guid id)
|
||||
{
|
||||
var localUser = UserMapper.GetUser(id);
|
||||
if (localUser != null)
|
||||
{
|
||||
return Response.AsJson(localUser);
|
||||
}
|
||||
|
||||
return Nancy.Response.NoBody;
|
||||
}
|
||||
|
||||
private async Task<Response> PlexDetails(string id)
|
||||
{
|
||||
var plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
if (!string.IsNullOrEmpty(plexSettings.PlexAuthToken))
|
||||
{
|
||||
//Get Plex Users
|
||||
var plexUsers = PlexApi.GetUsers(plexSettings.PlexAuthToken);
|
||||
|
||||
var selectedUser = plexUsers.User?.FirstOrDefault(x => x.Id.ToString() == id);
|
||||
if (selectedUser != null)
|
||||
{
|
||||
return Response.AsJson(selectedUser);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return Nancy.Response.NoBody;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all claims for the users.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private Response GetEnum<T>()
|
||||
{
|
||||
var retVal = new List<CheckBox>();
|
||||
foreach (var p in Enum.GetValues(typeof(T)))
|
||||
{
|
||||
var perm = (T)p;
|
||||
var displayValue = EnumHelper<T>.GetDisplayValue(perm);
|
||||
|
||||
retVal.Add(new CheckBox { Name = displayValue, Selected = false, Value = (int)p });
|
||||
}
|
||||
|
||||
return Response.AsJson(retVal);
|
||||
}
|
||||
|
||||
private UserManagementUsersViewModel MapLocalUser(UsersModel user, DateTime lastLoggedIn)
|
||||
{
|
||||
var features = (Features)user.Features;
|
||||
var permissions = (Permissions)user.Permissions;
|
||||
|
||||
var userProps = ByteConverterHelper.ReturnObject<UserProperties>(user.UserProperties);
|
||||
|
||||
var m = new UserManagementUsersViewModel
|
||||
{
|
||||
Id = user.UserGuid,
|
||||
PermissionsFormattedString = permissions == 0 ? "None" : permissions.ToString(),
|
||||
FeaturesFormattedString = features.ToString(),
|
||||
Username = user.UserName,
|
||||
Type = UserType.LocalUser,
|
||||
EmailAddress = userProps.EmailAddress,
|
||||
Alias = userProps.UserAlias,
|
||||
LastLoggedIn = lastLoggedIn,
|
||||
};
|
||||
|
||||
|
||||
m.Permissions.AddRange(GetPermissions(permissions));
|
||||
m.Features.AddRange(GetFeatures(features));
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
private UserManagementUsersViewModel MapPlexUser(UserFriends plexInfo, PlexUsers dbUser, DateTime lastLoggedIn)
|
||||
{
|
||||
if (dbUser == null)
|
||||
{
|
||||
dbUser = new PlexUsers();
|
||||
}
|
||||
var features = (Features)dbUser?.Features;
|
||||
var permissions = (Permissions)dbUser?.Permissions;
|
||||
|
||||
var m = new UserManagementUsersViewModel
|
||||
{
|
||||
Id = plexInfo.Id,
|
||||
PermissionsFormattedString = permissions == 0 ? "None" : permissions.ToString(),
|
||||
FeaturesFormattedString = features.ToString(),
|
||||
Username = plexInfo.Username,
|
||||
Type = UserType.PlexUser,
|
||||
EmailAddress = plexInfo.Email,
|
||||
Alias = dbUser?.UserAlias ?? string.Empty,
|
||||
LastLoggedIn = lastLoggedIn,
|
||||
PlexInfo = new UserManagementPlexInformation
|
||||
{
|
||||
Thumb = plexInfo.Thumb
|
||||
},
|
||||
};
|
||||
|
||||
m.Permissions.AddRange(GetPermissions(permissions));
|
||||
m.Features.AddRange(GetFeatures(features));
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
private UserManagementUsersViewModel MapPlexAdmin(PlexAccount plexInfo, PlexUsers dbUser, DateTime lastLoggedIn)
|
||||
{
|
||||
if (dbUser == null)
|
||||
{
|
||||
dbUser = new PlexUsers();
|
||||
}
|
||||
var features = (Features)dbUser?.Features;
|
||||
var permissions = (Permissions)dbUser?.Permissions;
|
||||
|
||||
var m = new UserManagementUsersViewModel
|
||||
{
|
||||
Id = plexInfo.Id,
|
||||
PermissionsFormattedString = permissions == 0 ? "None" : permissions.ToString(),
|
||||
FeaturesFormattedString = features.ToString(),
|
||||
Username = plexInfo.Username,
|
||||
Type = UserType.PlexUser,
|
||||
EmailAddress = plexInfo.Email,
|
||||
Alias = dbUser?.UserAlias ?? string.Empty,
|
||||
LastLoggedIn = lastLoggedIn,
|
||||
};
|
||||
|
||||
m.Permissions.AddRange(GetPermissions(permissions));
|
||||
m.Features.AddRange(GetFeatures(features));
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
private List<CheckBox> GetPermissions(Permissions permissions)
|
||||
{
|
||||
var retVal = new List<CheckBox>();
|
||||
// Add permissions
|
||||
foreach (var p in Enum.GetValues(typeof(Permissions)))
|
||||
{
|
||||
var perm = (Permissions)p;
|
||||
var displayValue = EnumHelper<Permissions>.GetDisplayValue(perm);
|
||||
var pm = new CheckBox
|
||||
{
|
||||
Name = displayValue,
|
||||
Selected = permissions.HasFlag(perm),
|
||||
Value = (int)perm
|
||||
};
|
||||
|
||||
retVal.Add(pm);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private List<CheckBox> GetFeatures(Features features)
|
||||
{
|
||||
var retVal = new List<CheckBox>();
|
||||
// Add features
|
||||
foreach (var p in Enum.GetValues(typeof(Features)))
|
||||
{
|
||||
var perm = (Features)p;
|
||||
var displayValue = EnumHelper<Features>.GetDisplayValue(perm);
|
||||
var pm = new CheckBox
|
||||
{
|
||||
Name = displayValue,
|
||||
Selected = features.HasFlag(perm),
|
||||
Value = (int)perm
|
||||
};
|
||||
|
||||
retVal.Add(pm);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
203
Ombi.UI/Modules/UserWizardModule.cs
Normal file
203
Ombi.UI/Modules/UserWizardModule.cs
Normal file
|
@ -0,0 +1,203 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: UserWizardModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Nancy;
|
||||
using Nancy.Extensions;
|
||||
using Nancy.ModelBinding;
|
||||
using Nancy.Validation;
|
||||
using NLog;
|
||||
using Ombi.Api.Interfaces;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Helpers.Analytics;
|
||||
using Ombi.Helpers.Permissions;
|
||||
using Ombi.UI.Authentication;
|
||||
using Ombi.UI.Helpers;
|
||||
using Ombi.UI.Models;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
using Action = Ombi.Helpers.Analytics.Action;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class UserWizardModule : BaseModule
|
||||
{
|
||||
public UserWizardModule(ISettingsService<PlexRequestSettings> pr, ISettingsService<PlexSettings> plex, IPlexApi plexApi,
|
||||
ISettingsService<AuthenticationSettings> auth, ICustomUserMapper m, IAnalytics a, ISecurityExtensions security) : base("wizard", pr, security)
|
||||
{
|
||||
PlexSettings = plex;
|
||||
PlexApi = plexApi;
|
||||
PlexRequestSettings = pr;
|
||||
Auth = auth;
|
||||
Mapper = m;
|
||||
Analytics = a;
|
||||
|
||||
Get["/", true] = async (x, ct) =>
|
||||
{
|
||||
a.TrackEventAsync(Category.Wizard, Action.Start, "Started the wizard", Username, CookieHelper.GetAnalyticClientId(Cookies));
|
||||
|
||||
var settings = await PlexRequestSettings.GetSettingsAsync();
|
||||
|
||||
if (settings.Wizard)
|
||||
{
|
||||
return Context.GetRedirect("~/search");
|
||||
}
|
||||
return View["Index"];
|
||||
};
|
||||
Post["/plexAuth"] = x => PlexAuth();
|
||||
Post["/plex", true] = async (x, ct) => await Plex();
|
||||
Post["/plexrequest", true] = async (x, ct) => await PlexRequest();
|
||||
Post["/auth", true] = async (x, ct) => await Authentication();
|
||||
Post["/createuser",true] = async (x,ct) => await CreateUser();
|
||||
}
|
||||
|
||||
private ISettingsService<PlexSettings> PlexSettings { get; }
|
||||
private IPlexApi PlexApi { get; }
|
||||
private ISettingsService<PlexRequestSettings> PlexRequestSettings { get; }
|
||||
private ISettingsService<AuthenticationSettings> Auth { get; }
|
||||
private ICustomUserMapper Mapper { get; }
|
||||
private IAnalytics Analytics { get; }
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
|
||||
private Response PlexAuth()
|
||||
{
|
||||
var user = this.Bind<PlexAuth>();
|
||||
|
||||
if (string.IsNullOrEmpty(user.username) || string.IsNullOrEmpty(user.password))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Please provide a valid username and password" });
|
||||
}
|
||||
|
||||
var model = PlexApi.SignIn(user.username, user.password);
|
||||
|
||||
if (model?.user == null)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Incorrect username or password!" });
|
||||
}
|
||||
|
||||
// Set the auth token in the session so we can use it in the next form
|
||||
Session[SessionKeys.UserWizardPlexAuth] = model.user.authentication_token;
|
||||
|
||||
var servers = PlexApi.GetServer(model.user.authentication_token);
|
||||
var firstServer = servers.Server.FirstOrDefault();
|
||||
|
||||
return Response.AsJson(new { Result = true, firstServer?.Port, Ip = firstServer?.LocalAddresses, firstServer?.Scheme });
|
||||
}
|
||||
|
||||
private async Task<Response> Plex()
|
||||
{
|
||||
var form = this.Bind<PlexSettings>();
|
||||
var valid = this.Validate(form);
|
||||
if (!valid.IsValid)
|
||||
{
|
||||
return Response.AsJson(valid.SendJsonError());
|
||||
}
|
||||
form.PlexAuthToken = Session[SessionKeys.UserWizardPlexAuth].ToString(); // Set the auth token from the previous form
|
||||
|
||||
// Get the machine ID from the settings (This could have changed)
|
||||
try
|
||||
{
|
||||
var servers = PlexApi.GetServer(form.PlexAuthToken);
|
||||
var firstServer = servers.Server.FirstOrDefault(x => x.AccessToken == form.PlexAuthToken);
|
||||
|
||||
Session[SessionKeys.UserWizardMachineId] = firstServer?.MachineIdentifier;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Probably bad settings, just continue
|
||||
Log.Error(e);
|
||||
}
|
||||
|
||||
var result = await PlexSettings.SaveSettingsAsync(form);
|
||||
if (result)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = true });
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not save the settings to the database, please try again." });
|
||||
}
|
||||
|
||||
private async Task<Response> PlexRequest()
|
||||
{
|
||||
var form = this.Bind<PlexRequestSettings>();
|
||||
var valid = this.Validate(form);
|
||||
if (!valid.IsValid)
|
||||
{
|
||||
return Response.AsJson(valid.SendJsonError());
|
||||
}
|
||||
var currentSettings = await PlexRequestSettings.GetSettingsAsync();
|
||||
currentSettings.SearchForMovies = form.SearchForMovies;
|
||||
currentSettings.SearchForTvShows = form.SearchForTvShows;
|
||||
currentSettings.SearchForMusic = form.SearchForMusic;
|
||||
|
||||
var result = await PlexRequestSettings.SaveSettingsAsync(currentSettings);
|
||||
if (result)
|
||||
{
|
||||
return Response.AsJson(new { Result = true });
|
||||
}
|
||||
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not save the settings to the database, please try again." });
|
||||
}
|
||||
|
||||
private async Task<Response> Authentication()
|
||||
{
|
||||
var form = this.Bind<AuthenticationSettings>();
|
||||
|
||||
var result = await Auth.SaveSettingsAsync(form);
|
||||
if (result)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = true });
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not save the settings to the database, please try again." });
|
||||
}
|
||||
|
||||
private async Task<Response> CreateUser()
|
||||
{
|
||||
var username = (string)Request.Form.Username;
|
||||
var userId = Mapper.CreateUser(username, Request.Form.Password, EnumHelper<Permissions>.All() - (int)Permissions.ReadOnlyUser, 0);
|
||||
Analytics.TrackEventAsync(Category.Wizard, Action.Finish, "Finished the wizard", username, CookieHelper.GetAnalyticClientId(Cookies));
|
||||
Session[SessionKeys.UsernameKey] = username;
|
||||
|
||||
// Destroy the Plex Auth Token
|
||||
Session.Delete(SessionKeys.UserWizardPlexAuth);
|
||||
|
||||
// Update the settings so we know we have been through the wizard
|
||||
var settings = await PlexRequestSettings.GetSettingsAsync();
|
||||
settings.Wizard = true;
|
||||
await PlexRequestSettings.SaveSettingsAsync(settings);
|
||||
|
||||
var baseUrl = string.IsNullOrEmpty(settings.BaseUrl) ? string.Empty : $"/{settings.BaseUrl}";
|
||||
|
||||
return CustomModuleExtensions.LoginAndRedirect(this,(Guid)userId, fallbackRedirectUrl: $"{baseUrl}/search");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue