#region Copyright // /************************************************************************ // Copyright (c) 2016 Jamie Rees // File: AdminModule.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.Net; using System.Threading.Tasks; using Nancy; using Nancy.Extensions; using Nancy.Json; using Nancy.ModelBinding; using Nancy.Responses.Negotiation; using Nancy.Validation; using NLog; using Ombi.Api; 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.Exceptions; using Ombi.Helpers.Permissions; using Ombi.Services.Interfaces; using Ombi.Services.Jobs; using Ombi.Services.Notification; using Ombi.Store.Models; using Ombi.Store.Repository; using Ombi.UI.Helpers; using Ombi.UI.Models; using Quartz; using Action = Ombi.Helpers.Analytics.Action; using ISecurityExtensions = Ombi.Core.ISecurityExtensions; namespace Ombi.UI.Modules.Admin { public class AdminModule : BaseModule { private ISettingsService PrService { get; } private ISettingsService CpService { get; } private ISettingsService AuthService { get; } private ISettingsService PlexService { get; } private ISettingsService SonarrService { get; } private ISettingsService SickRageService { get; } private ISettingsService EmailService { get; } private ISettingsService PushbulletService { get; } private ISettingsService PushoverService { get; } private ISettingsService HeadphonesService { get; } private ISettingsService NewsLetterService { get; } private ISettingsService WatcherSettings { get; } private ISettingsService LogService { get; } private IPlexApi PlexApi { get; } private ISonarrApi SonarrApi { get; } private IPushbulletApi PushbulletApi { get; } private IPushoverApi PushoverApi { get; } private ICouchPotatoApi CpApi { get; } private IRepository LogsRepo { get; } private INotificationService NotificationService { get; } private ICacheProvider Cache { get; } private ISettingsService SlackSettings { get; } private ISettingsService LandingSettings { get; } private ISettingsService ScheduledJobSettings { get; } private ISlackApi SlackApi { get; } private IJobRecord JobRecorder { get; } private IAnalytics Analytics { get; } private IRecentlyAdded RecentlyAdded { get; } private ISettingsService NotifySettings { get; } private ISettingsService DiscordSettings { get; } private IDiscordApi DiscordApi { get; } private ISettingsService RadarrSettings { get; } private IRadarrApi RadarrApi { get; } private ISettingsService EmbySettings { get; } private IEmbyApi EmbyApi { get; } private static Logger Log = LogManager.GetCurrentClassLogger(); public AdminModule(ISettingsService prService, ISettingsService cpService, ISettingsService auth, ISettingsService plex, ISettingsService sonarr, ISettingsService sickrage, ISonarrApi sonarrApi, ISettingsService email, IPlexApi plexApi, ISettingsService pbSettings, PushbulletApi pbApi, ICouchPotatoApi cpApi, ISettingsService pushoverSettings, ISettingsService newsletter, IPushoverApi pushoverApi, IRepository logsRepo, INotificationService notify, ISettingsService headphones, ISettingsService logs, ICacheProvider cache, ISettingsService slackSettings, ISlackApi slackApi, ISettingsService lp, ISettingsService scheduler, IJobRecord rec, IAnalytics analytics, ISettingsService notifyService, IRecentlyAdded recentlyAdded, ISettingsService watcherSettings , ISettingsService discord, IDiscordApi discordapi, ISettingsService settings, IRadarrApi radarrApi, ISettingsService embySettings, IEmbyApi emby , ISecurityExtensions security) : base("admin", prService, security) { PrService = prService; CpService = cpService; AuthService = auth; PlexService = plex; SonarrService = sonarr; SonarrApi = sonarrApi; EmailService = email; PlexApi = plexApi; PushbulletService = pbSettings; PushbulletApi = pbApi; CpApi = cpApi; SickRageService = sickrage; LogsRepo = logsRepo; PushoverService = pushoverSettings; PushoverApi = pushoverApi; NotificationService = notify; HeadphonesService = headphones; NewsLetterService = newsletter; LogService = logs; Cache = cache; SlackSettings = slackSettings; SlackApi = slackApi; LandingSettings = lp; ScheduledJobSettings = scheduler; JobRecorder = rec; Analytics = analytics; NotifySettings = notifyService; RecentlyAdded = recentlyAdded; WatcherSettings = watcherSettings; DiscordSettings = discord; DiscordApi = discordapi; RadarrSettings = settings; RadarrApi = radarrApi; EmbyApi = emby; EmbySettings = embySettings; Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx); Get["/"] = _ => Admin(); Get["/authentication", true] = async (x, ct) => await Authentication(); Post["/authentication", true] = async (x, ct) => await SaveAuthentication(); Post["/", true] = async (x, ct) => await SaveAdmin(); Post["/requestauth"] = _ => RequestAuthToken(); Get["/getusers"] = _ => GetUsers(); Get["/couchpotato"] = _ => CouchPotato(); Post["/couchpotato", true] = async (x, ct) => await SaveCouchPotato(); Get["/plex"] = _ => Plex(); Post["/plex", true] = async (x, ct) => await SavePlex(); Get["/emby", true] = async (x, ct) => await Emby(); Post["/emby", true] = async (x, ct) => await SaveEmby(); Get["/sonarr"] = _ => Sonarr(); Post["/sonarr"] = _ => SaveSonarr(); Post["/sonarrprofiles"] = _ => GetSonarrQualityProfiles(); Get["/sickrage"] = _ => Sickrage(); Post["/sickrage"] = _ => SaveSickrage(); Post["/cpprofiles", true] = async (x, ct) => await GetCpProfiles(); Post["/cpapikey"] = x => GetCpApiKey(); Get["/emailnotification"] = _ => EmailNotifications(); Post["/emailnotification"] = _ => SaveEmailNotifications(); Post["/testemailnotification", true] = async (x, ct) => await TestEmailNotifications(); Get["/pushbulletnotification"] = _ => PushbulletNotifications(); Post["/pushbulletnotification"] = _ => SavePushbulletNotifications(); Post["/testpushbulletnotification", true] = async (x, ct) => await TestPushbulletNotifications(); Get["/pushovernotification"] = _ => PushoverNotifications(); Post["/pushovernotification"] = _ => SavePushoverNotifications(); Post["/testpushovernotification", true] = async (x, ct) => await TestPushoverNotifications(); Get["/logs"] = _ => Logs(); Get["/loglevel"] = _ => GetLogLevels(); Post["/loglevel"] = _ => UpdateLogLevels(Request.Form.level); Get["/loadlogs"] = _ => LoadLogs(); Get["/headphones"] = _ => Headphones(); Post["/headphones"] = _ => SaveHeadphones(); Get["/newsletter", true] = async (x, ct) => await Newsletter(); Post["/newsletter", true] = async (x, ct) => await SaveNewsletter(); Post["/createapikey"] = x => CreateApiKey(); Post["/testslacknotification", true] = async (x, ct) => await TestSlackNotification(); Get["/slacknotification"] = _ => SlackNotifications(); Post["/slacknotification"] = _ => SaveSlackNotifications(); Post["/testdiscordnotification", true] = async (x, ct) => await TestDiscordNotification(); Get["/discordnotification", true] = async (x, ct) => await DiscordNotification(); Post["/discordnotification", true] = async (x, ct) => await SaveDiscordNotifications(); Get["/landingpage", true] = async (x, ct) => await LandingPage(); Post["/landingpage", true] = async (x, ct) => await SaveLandingPage(); Get["/scheduledjobs", true] = async (x, ct) => await GetScheduledJobs(); Post["/scheduledjobs", true] = async (x, ct) => await SaveScheduledJobs(); Post["/clearlogs", true] = async (x, ct) => await ClearLogs(); Get["/notificationsettings", true] = async (x, ct) => await NotificationSettings(); Post["/notificationsettings"] = x => SaveNotificationSettings(); Post["/recentlyAddedTest"] = x => RecentlyAddedTest(); } private async Task Authentication() { var settings = await AuthService.GetSettingsAsync(); return View["/Authentication", settings]; } private async Task SaveAuthentication() { var model = this.Bind(); var result = await AuthService.SaveSettingsAsync(model); if (result) { if (!string.IsNullOrEmpty(BaseUrl)) { return Context.GetRedirect($"~/{BaseUrl}/admin/authentication"); } return Context.GetRedirect("~/admin/authentication"); } if (!string.IsNullOrEmpty(BaseUrl)) { return Context.GetRedirect($"~/{BaseUrl}/error"); //TODO create error page } return Context.GetRedirect("~/error"); //TODO create error page } private Negotiator Admin() { var settings = PrService.GetSettings(); return View["Settings", settings]; } private async Task SaveAdmin() { var model = this.Bind(); var valid = this.Validate(model); if (!valid.IsValid) { return Response.AsJson(valid.SendJsonError()); } model.Wizard = true; if (!string.IsNullOrWhiteSpace(model.BaseUrl)) { if (model.BaseUrl.StartsWith("/", StringComparison.CurrentCultureIgnoreCase) || model.BaseUrl.StartsWith("\\", StringComparison.CurrentCultureIgnoreCase)) { model.BaseUrl = model.BaseUrl.Remove(0, 1); } } if (!model.CollectAnalyticData) { Analytics.TrackEventAsync(Category.Admin, Action.Save, "CollectAnalyticData turned off", Username, CookieHelper.GetAnalyticClientId(Cookies)); } var result = await PrService.SaveSettingsAsync(model); Analytics.TrackEventAsync(Category.Admin, Action.Save, "PlexRequestSettings", Username, CookieHelper.GetAnalyticClientId(Cookies)); return Response.AsJson(result ? new JsonResponseModel { Result = true } : new JsonResponseModel { Result = false, Message = "We could not save to the database, please try again" }); } private Response RequestAuthToken() { var user = this.Bind(); if (string.IsNullOrEmpty(user.username) || string.IsNullOrEmpty(user.password)) { return Response.AsJson(new { 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 { Result = false, Message = "Incorrect username or password!" }); } var oldSettings = PlexService.GetSettings(); if (oldSettings != null) { oldSettings.PlexAuthToken = model.user.authentication_token; PlexService.SaveSettings(oldSettings); } else { var newModel = new PlexSettings { PlexAuthToken = model.user.authentication_token }; PlexService.SaveSettings(newModel); } return Response.AsJson(new { Result = true, AuthToken = model.user.authentication_token }); } private Response GetUsers() { var settings = PlexService.GetSettings(); var token = settings?.PlexAuthToken; if (token == null) { return Response.AsJson(new { Result = true, Users = string.Empty }); } try { var users = PlexApi.GetUsers(token); if (users == null) { return Response.AsJson(string.Empty); } if (users.User == null || users.User?.Length == 0) { return Response.AsJson(string.Empty); } var usernames = users.User.Select(x => x.Title); return Response.AsJson(new { Result = true, Users = usernames }); } catch (Exception ex) { Log.Error(ex); if (ex is WebException || ex is ApiRequestException) { return Response.AsJson(new { Result = false, Message = "Could not load the user list! We have connectivity problems connecting to Plex, Please ensure we can access Plex.Tv, The error has been logged." }); } return Response.AsJson(new { Result = false, Message = ex.Message }); } } private Negotiator CouchPotato() { var settings = CpService.GetSettings(); return View["CouchPotato", settings]; } private async Task SaveCouchPotato() { var couchPotatoSettings = this.Bind(); var valid = this.Validate(couchPotatoSettings); if (!valid.IsValid) { return Response.AsJson(valid.SendJsonError()); } var watcherSettings = await WatcherSettings.GetSettingsAsync(); if (watcherSettings.Enabled) { return Response.AsJson(new JsonResponseModel { Result = false, Message = "Cannot have Watcher and CouchPotato both enabled." }); } var radarrSettings = await RadarrSettings.GetSettingsAsync(); if (radarrSettings.Enabled) { return Response.AsJson(new JsonResponseModel { Result = false, Message = "Cannot have Radarr and CouchPotato both enabled." }); } couchPotatoSettings.ApiKey = couchPotatoSettings.ApiKey.Trim(); var result = await CpService.SaveSettingsAsync(couchPotatoSettings); return Response.AsJson(result ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for CouchPotato!" } : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); } private Negotiator Plex() { var settings = PlexService.GetSettings(); return View["Plex", settings]; } private async Task SavePlex() { var plexSettings = this.Bind(); var valid = this.Validate(plexSettings); if (!valid.IsValid) { return Response.AsJson(valid.SendJsonError()); } if (plexSettings.Enable) { var embySettings = await EmbySettings.GetSettingsAsync(); if (embySettings.Enable) { return Response.AsJson(new JsonResponseModel { Result = false, Message = "Emby is enabled, we cannot enable Plex and Emby" }); } } if (string.IsNullOrEmpty(plexSettings.MachineIdentifier)) { //Lookup identifier var server = PlexApi.GetServer(plexSettings.PlexAuthToken); plexSettings.MachineIdentifier = server.Server.FirstOrDefault(x => x.AccessToken == plexSettings.PlexAuthToken)?.MachineIdentifier; } var result = await PlexService.SaveSettingsAsync(plexSettings); return Response.AsJson(result ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Plex!" } : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); } private async Task Emby() { var settings = await EmbySettings.GetSettingsAsync(); return View["Emby", settings]; } private async Task SaveEmby() { var emby = this.Bind(); var valid = this.Validate(emby); if (!valid.IsValid) { return Response.AsJson(valid.SendJsonError()); } if (emby.Enable) { var plexSettings = await PlexService.GetSettingsAsync(); if (plexSettings.Enable) { return Response.AsJson(new JsonResponseModel { Result = false, Message = "Plex is enabled, we cannot enable Plex and Emby" }); } } // Get the users var users = EmbyApi.GetUsers(emby.FullUri, emby.ApiKey); // Find admin var admin = users.FirstOrDefault(x => x.Policy.IsAdministrator); emby.AdministratorId = admin?.Id; var result = await EmbySettings.SaveSettingsAsync(emby); return Response.AsJson(result ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Emby!" } : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); } private Negotiator Sonarr() { var settings = SonarrService.GetSettings(); return View["Sonarr", settings]; } private Response SaveSonarr() { var sonarrSettings = this.Bind(); var valid = this.Validate(sonarrSettings); if (!valid.IsValid) { return Response.AsJson(valid.SendJsonError()); } var sickRageEnabled = SickRageService.GetSettings().Enabled; if (sickRageEnabled) { return Response.AsJson(new JsonResponseModel { Result = false, Message = "SickRage is enabled, we cannot enable Sonarr and SickRage" }); } sonarrSettings.ApiKey = sonarrSettings.ApiKey.Trim(); var result = SonarrService.SaveSettings(sonarrSettings); return Response.AsJson(result ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Sonarr!" } : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); } private Negotiator Sickrage() { var settings = SickRageService.GetSettings(); return View["Sickrage", settings]; } private Response SaveSickrage() { var sickRageSettings = this.Bind(); var valid = this.Validate(sickRageSettings); if (!valid.IsValid) { return Response.AsJson(valid.SendJsonError()); } var sonarrEnabled = SonarrService.GetSettings().Enabled; if (sonarrEnabled) { return Response.AsJson(new JsonResponseModel { Result = false, Message = "Sonarr is enabled, we cannot enable Sonarr and SickRage" }); } sickRageSettings.ApiKey = sickRageSettings.ApiKey.Trim(); var result = SickRageService.SaveSettings(sickRageSettings); return Response.AsJson(result ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for SickRage!" } : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); } private Response GetSonarrQualityProfiles() { var settings = this.Bind(); var profiles = SonarrApi.GetProfiles(settings.ApiKey, settings.FullUri); // set the cache if (profiles != null) { Cache.Set(CacheKeys.SonarrQualityProfiles, profiles); } return Response.AsJson(profiles); } private Negotiator EmailNotifications() { var settings = EmailService.GetSettings(); return View["EmailNotifications", settings]; } private async Task TestEmailNotifications() { var settings = this.Bind(); var valid = this.Validate(settings); if (!valid.IsValid) { return Response.AsJson(valid.SendJsonError()); } var currentSettings = await EmailService.GetSettingsAsync(); var notificationModel = new NotificationModel { NotificationType = NotificationType.Test, DateTime = DateTime.Now, ImgSrc = "http://3.bp.blogspot.com/-EFM-XoKoZ0o/UznF567wCRI/AAAAAAAAALM/6ut7MCF2LrU/s1600/xkcd.png" }; try { NotificationService.Subscribe(new EmailMessageNotification(EmailService)); settings.Enabled = true; await NotificationService.Publish(notificationModel, settings); Log.Info("Sent email notification test"); } catch (Exception) { Log.Error("Failed to subscribe and publish test Email Notification"); } finally { if (!currentSettings.Enabled) { NotificationService.UnSubscribe(new EmailMessageNotification(EmailService)); } } return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Email Notification!" }); } private Response SaveEmailNotifications() { var settings = this.Bind(); var valid = this.Validate(settings); if (!valid.IsValid) { return Response.AsJson(valid.SendJsonError()); } if (settings.Authentication) { if (string.IsNullOrEmpty(settings.EmailUsername) || string.IsNullOrEmpty(settings.EmailPassword)) { return Response.AsJson(new JsonResponseModel { Result = false, Message = "SMTP Authentication is enabled, please specify a username and password" }); } } var result = EmailService.SaveSettings(settings); if (settings.Enabled) { NotificationService.Subscribe(new EmailMessageNotification(EmailService)); } else { NotificationService.UnSubscribe(new EmailMessageNotification(EmailService)); } Log.Info("Saved email settings, result: {0}", result); return Response.AsJson(result ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Email Notifications!" } : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); } private Negotiator PushbulletNotifications() { var settings = PushbulletService.GetSettings(); return View["PushbulletNotifications", settings]; } private Response SavePushbulletNotifications() { var settings = this.Bind(); var valid = this.Validate(settings); if (!valid.IsValid) { return Response.AsJson(valid.SendJsonError()); } var result = PushbulletService.SaveSettings(settings); if (settings.Enabled) { NotificationService.Subscribe(new PushbulletNotification(PushbulletApi, PushbulletService)); } else { NotificationService.UnSubscribe(new PushbulletNotification(PushbulletApi, PushbulletService)); } Log.Info("Saved email settings, result: {0}", result); return Response.AsJson(result ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Pushbullet Notifications!" } : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); } private async Task TestPushbulletNotifications() { var settings = this.Bind(); var valid = this.Validate(settings); if (!valid.IsValid) { return Response.AsJson(valid.SendJsonError()); } var notificationModel = new NotificationModel { NotificationType = NotificationType.Test, DateTime = DateTime.Now }; var currentSettings = await PushbulletService.GetSettingsAsync(); try { NotificationService.Subscribe(new PushbulletNotification(PushbulletApi, PushbulletService)); settings.Enabled = true; await NotificationService.Publish(notificationModel, settings); Log.Info("Sent pushbullet notification test"); } catch (Exception) { Log.Error("Failed to subscribe and publish test Pushbullet Notification"); } finally { if (!currentSettings.Enabled) { NotificationService.UnSubscribe(new PushbulletNotification(PushbulletApi, PushbulletService)); } } return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Pushbullet Notification!" }); } private Negotiator PushoverNotifications() { var settings = PushoverService.GetSettings(); return View["PushoverNotifications", settings]; } private Response SavePushoverNotifications() { var settings = this.Bind(); var valid = this.Validate(settings); if (!valid.IsValid) { return Response.AsJson(valid.SendJsonError()); } var result = PushoverService.SaveSettings(settings); if (settings.Enabled) { NotificationService.Subscribe(new PushoverNotification(PushoverApi, PushoverService)); } else { NotificationService.UnSubscribe(new PushoverNotification(PushoverApi, PushoverService)); } Log.Info("Saved email settings, result: {0}", result); return Response.AsJson(result ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Pushover Notifications!" } : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); } private async Task TestPushoverNotifications() { var settings = this.Bind(); var valid = this.Validate(settings); if (!valid.IsValid) { return Response.AsJson(valid.SendJsonError()); } var notificationModel = new NotificationModel { NotificationType = NotificationType.Test, DateTime = DateTime.Now }; var currentSettings = await PushoverService.GetSettingsAsync(); try { NotificationService.Subscribe(new PushoverNotification(PushoverApi, PushoverService)); settings.Enabled = true; await NotificationService.Publish(notificationModel, settings); Log.Info("Sent pushover notification test"); } catch (Exception) { Log.Error("Failed to subscribe and publish test Pushover Notification"); } finally { if (!currentSettings.Enabled) { NotificationService.UnSubscribe(new PushoverNotification(PushoverApi, PushoverService)); } } return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Pushover Notification!" }); } private async Task GetCpProfiles() { var settings = this.Bind(); var valid = this.Validate(settings); if (!valid.IsValid) { return Response.AsJson(valid.SendJsonError()); } var profiles = CpApi.GetProfiles(settings.FullUri, settings.ApiKey); // set the cache if (profiles != null) { Cache.Set(CacheKeys.CouchPotatoQualityProfiles, profiles); } // Save the first profile found (user might not press save...) settings.ProfileId = profiles?.list?.FirstOrDefault()?._id; await CpService.SaveSettingsAsync(settings); return Response.AsJson(profiles); } private Response GetCpApiKey() { var settings = this.Bind(); if (string.IsNullOrEmpty(settings.Username) || string.IsNullOrEmpty(settings.Password)) { return Response.AsJson(new { Message = "Please enter a username and password to request the Api Key", Result = false }); } var key = CpApi.GetApiKey(settings.FullUri, settings.Username, settings.Password); return Response.AsJson(key); } private Negotiator Logs() { var model = false; if (Request.Query["developer"] != null) model = true; return View["Logs", model]; } private Response LoadLogs() { JsonSettings.MaxJsonLength = int.MaxValue; var allLogs = LogsRepo.GetAll().OrderByDescending(x => x.Id).Take(200); var model = new DatatablesModel { Data = new List() }; foreach (var l in allLogs) { l.DateString = l.Date.ToString("G"); model.Data.Add(l); } return Response.AsJson(model); } private Response GetLogLevels() { var levels = LogManager.Configuration.LoggingRules.FirstOrDefault(x => x.NameMatches("database")); return Response.AsJson(levels.Levels); } private Response UpdateLogLevels(int level) { var settings = LogService.GetSettings(); Analytics.TrackEventAsync(Category.Admin, Action.Update, "Updated Log Levels", Username, CookieHelper.GetAnalyticClientId(Cookies), level); // apply the level var newLevel = LogLevel.FromOrdinal(level); LoggingHelper.ReconfigureLogLevel(newLevel); //save the log settings settings.Level = level; LogService.SaveSettings(settings); return Response.AsJson(new JsonResponseModel { Result = true, Message = $"The new log level is now {newLevel}" }); } private Negotiator Headphones() { var settings = HeadphonesService.GetSettings(); return View["Headphones", settings]; } private Response SaveHeadphones() { var settings = this.Bind(); var valid = this.Validate(settings); if (!valid.IsValid) { var error = valid.SendJsonError(); Log.Info("Error validating Headphones settings, message: {0}", error.Message); return Response.AsJson(error); } settings.ApiKey = settings.ApiKey.Trim(); var result = HeadphonesService.SaveSettings(settings); Log.Info("Saved headphones settings, result: {0}", result); return Response.AsJson(result ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Headphones!" } : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); } private async Task Newsletter() { var settings = await NewsLetterService.GetSettingsAsync(); return View["NewsletterSettings", settings]; } private async Task SaveNewsletter() { var settings = this.Bind(); var valid = this.Validate(settings); if (!valid.IsValid) { var error = valid.SendJsonError(); Log.Info("Error validating Newsletter settings, message: {0}", error.Message); return Response.AsJson(error); } // Make sure emails are setup var emailSettings = await EmailService.GetSettingsAsync(); if (!emailSettings.Enabled) { return Response.AsJson(new JsonResponseModel { Result = false, Message = "Please enable your email notifications" }); } settings.SendRecentlyAddedEmail = settings.SendRecentlyAddedEmail; var result = NewsLetterService.SaveSettings(settings); Log.Info("Saved headphones settings, result: {0}", result); return Response.AsJson(result ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Newsletter!" } : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); } private Response CreateApiKey() { Analytics.TrackEventAsync(Category.Admin, Action.Create, "Created API Key", Username, CookieHelper.GetAnalyticClientId(Cookies)); var apiKey = Guid.NewGuid().ToString("N"); var settings = PrService.GetSettings(); settings.ApiKey = apiKey; PrService.SaveSettings(settings); return Response.AsJson(apiKey); } private async Task TestSlackNotification() { var settings = this.BindAndValidate(); if (!ModelValidationResult.IsValid) { return Response.AsJson(ModelValidationResult.SendJsonError()); } var notificationModel = new NotificationModel { NotificationType = NotificationType.Test, DateTime = DateTime.Now }; var currentSlackSettings = await SlackSettings.GetSettingsAsync(); try { NotificationService.Subscribe(new SlackNotification(SlackApi, SlackSettings)); settings.Enabled = true; await NotificationService.Publish(notificationModel, settings); Log.Info("Sent slack notification test"); } catch (Exception e) { Log.Error(e, "Failed to subscribe and publish test Slack Notification"); } finally { if (!currentSlackSettings.Enabled) { NotificationService.UnSubscribe(new SlackNotification(SlackApi, SlackSettings)); } } return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Slack Notification! If you do not receive it please check the logs." }); } private Negotiator SlackNotifications() { var settings = SlackSettings.GetSettings(); return View["SlackNotifications", settings]; } private Response SaveSlackNotifications() { var settings = this.BindAndValidate(); if (!ModelValidationResult.IsValid) { return Response.AsJson(ModelValidationResult.SendJsonError()); } var result = SlackSettings.SaveSettings(settings); if (settings.Enabled) { NotificationService.Subscribe(new SlackNotification(SlackApi, SlackSettings)); } else { NotificationService.UnSubscribe(new SlackNotification(SlackApi, SlackSettings)); } Log.Info("Saved slack settings, result: {0}", result); return Response.AsJson(result ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Slack Notifications!" } : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); } private async Task DiscordNotification() { var settings = await DiscordSettings.GetSettingsAsync(); return View["DiscordNotification", settings]; } private async Task TestDiscordNotification() { var settings = this.BindAndValidate(); if (!ModelValidationResult.IsValid) { return Response.AsJson(ModelValidationResult.SendJsonError()); } var notificationModel = new NotificationModel { NotificationType = NotificationType.Test, DateTime = DateTime.Now }; var currentDicordSettings = await DiscordSettings.GetSettingsAsync(); try { NotificationService.Subscribe(new DiscordNotification(DiscordApi, DiscordSettings)); settings.Enabled = true; await NotificationService.Publish(notificationModel, settings); Log.Info("Sent Discord notification test"); } catch (Exception e) { Log.Error(e, "Failed to subscribe and publish test Discord Notification"); } finally { if (!currentDicordSettings.Enabled) { NotificationService.UnSubscribe(new DiscordNotification(DiscordApi, DiscordSettings)); } } return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Discord Notification! If you do not receive it please check the logs." }); } private async Task SaveDiscordNotifications() { var settings = this.BindAndValidate(); if (!ModelValidationResult.IsValid) { return Response.AsJson(ModelValidationResult.SendJsonError()); } var result = await DiscordSettings.SaveSettingsAsync(settings); if (settings.Enabled) { NotificationService.Subscribe(new DiscordNotification(DiscordApi, DiscordSettings)); } else { NotificationService.UnSubscribe(new DiscordNotification(DiscordApi, DiscordSettings)); } Log.Info("Saved discord settings, result: {0}", result); return Response.AsJson(result ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Discord Notifications!" } : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); } private async Task LandingPage() { var settings = await LandingSettings.GetSettingsAsync(); if (settings.NoticeEnd == DateTime.MinValue) { settings.NoticeEnd = DateTime.Now; } if (settings.NoticeStart == DateTime.MinValue) { settings.NoticeStart = DateTime.Now; } return View["LandingPage", settings]; } private async Task SaveLandingPage() { var settings = this.Bind(); Analytics.TrackEventAsync(Category.Admin, Action.Update, "Update Landing Page", Username, CookieHelper.GetAnalyticClientId(Cookies)); var plexSettings = await PlexService.GetSettingsAsync(); if (string.IsNullOrEmpty(plexSettings.Ip)) { return Response.AsJson(new JsonResponseModel { Result = false, Message = "We cannot enable the landing page if Plex is not setup!" }); } if (settings.Enabled && settings.EnabledNoticeTime && string.IsNullOrEmpty(settings.NoticeMessage)) { return Response.AsJson(new JsonResponseModel { Result = false, Message = "If you are going to enabled the notice, then we need a message!" }); } var result = await LandingSettings.SaveSettingsAsync(settings); return Response.AsJson(result ? new JsonResponseModel { Result = true } : new JsonResponseModel { Result = false, Message = "Could not save to Db Please check the logs" }); } private async Task GetScheduledJobs() { var s = await ScheduledJobSettings.GetSettingsAsync(); var allJobs = await JobRecorder.GetJobsAsync(); var emby = await EmbySettings.GetSettingsAsync(); var plex = await PlexService.GetSettingsAsync(); var dict = new Dictionary(); foreach (var j in allJobs) { DateTime dt; if (dict.TryGetValue(j.Name, out dt)) { // We already have the key... Somehow, we should have never got this record. } else { dict.Add(j.Name,j.LastRun); } } var model = new ScheduledJobsViewModel { Emby = emby.Enable, Plex = plex.Enable, CouchPotatoCacher = s.CouchPotatoCacher, PlexAvailabilityChecker = s.PlexAvailabilityChecker, SickRageCacher = s.SickRageCacher, SonarrCacher = s.SonarrCacher, StoreBackup = s.StoreBackup, StoreCleanup = s.StoreCleanup, JobRecorder = dict, RecentlyAddedCron = s.RecentlyAddedCron, PlexContentCacher = s.PlexContentCacher, FaultQueueHandler = s.FaultQueueHandler, PlexEpisodeCacher = s.PlexEpisodeCacher, PlexUserChecker = s.PlexUserChecker, UserRequestLimitResetter = s.UserRequestLimitResetter }; return View["SchedulerSettings", model]; } private async Task SaveScheduledJobs() { Analytics.TrackEventAsync(Category.Admin, Action.Update, "Update ScheduledJobs", Username, CookieHelper.GetAnalyticClientId(Cookies)); var settings = this.Bind(); if (!string.IsNullOrEmpty(settings.RecentlyAddedCron)) { // Validate CRON var isValid = CronExpression.IsValidExpression(settings.RecentlyAddedCron); if (!isValid) { return Response.AsJson(new JsonResponseModel { Result = false, Message = $"CRON {settings.RecentlyAddedCron} is not valid. Please ensure you are using a valid CRON." }); } } var result = await ScheduledJobSettings.SaveSettingsAsync(settings); return Response.AsJson(result ? new JsonResponseModel { Result = true } : new JsonResponseModel { Result = false, Message = "Could not save to Db Please check the logs" }); } private async Task ClearLogs() { try { Analytics.TrackEventAsync(Category.Admin, Action.Delete, "Clear Logs", Username, CookieHelper.GetAnalyticClientId(Cookies)); var allLogs = await LogsRepo.GetAllAsync(); foreach (var logEntity in allLogs) { await LogsRepo.DeleteAsync(logEntity); } return Response.AsJson(new JsonResponseModel { Result = true, Message = "Logs cleared successfully." }); } catch (Exception e) { Log.Error(e); return Response.AsJson(new JsonResponseModel { Result = false, Message = e.Message }); } } private async Task NotificationSettings() { var s = await NotifySettings.GetSettingsAsync(); return View["NotificationSettings", s]; } private Negotiator SaveNotificationSettings() { var model = this.Bind(); return View["NotificationSettings", model]; } private Response RecentlyAddedTest() { try { Log.Debug("Clicked TEST"); RecentlyAdded.Test(); return Response.AsJson(new JsonResponseModel { Result = true, Message = "Sent email to administrator" }); } catch (Exception e) { Log.Error(e); return Response.AsJson(new JsonResponseModel { Result = false, Message = e.Message }); } } } }