mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-20 21:33:15 -07:00
Lots more work :(
This commit is contained in:
parent
f706b36ae5
commit
396bc5588a
61 changed files with 1066 additions and 122 deletions
|
@ -1,16 +0,0 @@
|
||||||
using System;
|
|
||||||
using NUnit.Framework;
|
|
||||||
|
|
||||||
namespace Ombi.Core.Tests
|
|
||||||
{
|
|
||||||
[TestFixture]
|
|
||||||
public class MovieEngineTests
|
|
||||||
{
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Test()
|
|
||||||
{
|
|
||||||
Assert.IsTrue(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
|
||||||
<OutputType>Library</OutputType>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>netstandard1.6</TargetFramework>
|
|
||||||
<ApplicationIcon />
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<OutputTypeEx>library</OutputTypeEx>
|
|
||||||
<StartupObject />
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.TestPlatform.TestHost" Version="15.0.0" />
|
|
||||||
<PackageReference Include="NUnit" Version="3.6.1" />
|
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.7.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
|
@ -1,6 +1,5 @@
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using Ombi.Core.Claims;
|
using Ombi.Core.Claims;
|
||||||
using Ombi.Core.Settings.Models;
|
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
|
|
||||||
namespace Ombi.Core.Engine.Interfaces
|
namespace Ombi.Core.Engine.Interfaces
|
||||||
|
|
|
@ -4,22 +4,27 @@ using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Hangfire;
|
||||||
using Ombi.Api.TheMovieDb;
|
using Ombi.Api.TheMovieDb;
|
||||||
using Ombi.Core.Models.Requests;
|
using Ombi.Core.Models.Requests;
|
||||||
using Ombi.Core.Models.Search;
|
using Ombi.Core.Models.Search;
|
||||||
using Ombi.Core.Requests.Models;
|
using Ombi.Core.Requests.Models;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
|
using Ombi.Notifications;
|
||||||
|
using Ombi.Notifications.Models;
|
||||||
|
|
||||||
namespace Ombi.Core.Engine
|
namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
public class RequestEngine : BaseMediaEngine, IRequestEngine
|
public class RequestEngine : BaseMediaEngine, IRequestEngine
|
||||||
{
|
{
|
||||||
public RequestEngine(IMovieDbApi movieApi, IRequestService requestService, IPrincipal user) : base(user, requestService)
|
public RequestEngine(IMovieDbApi movieApi, IRequestService requestService, IPrincipal user, INotificationService notificationService) : base(user, requestService)
|
||||||
{
|
{
|
||||||
MovieApi = movieApi;
|
MovieApi = movieApi;
|
||||||
|
NotificationService = notificationService;
|
||||||
}
|
}
|
||||||
private IMovieDbApi MovieApi { get; }
|
private IMovieDbApi MovieApi { get; }
|
||||||
|
private INotificationService NotificationService { get; }
|
||||||
public async Task<RequestEngineResult> RequestMovie(SearchMovieViewModel model)
|
public async Task<RequestEngineResult> RequestMovie(SearchMovieViewModel model)
|
||||||
{
|
{
|
||||||
var movieInfo = await MovieApi.GetMovieInformation(model.Id);
|
var movieInfo = await MovieApi.GetMovieInformation(model.Id);
|
||||||
|
@ -133,15 +138,15 @@ namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
//Log.Fatal(e);
|
//Log.Fatal(e);
|
||||||
//await FaultQueue.QueueItemAsync(model, movieInfo.Id.ToString(), RequestType.Movie, FaultType.RequestFault, e.Message);
|
//await FaultQueue.QueueItemAsync(model, movieInfo.Id.ToString(), RequestType.Movie, FaultType.RequestFault, e.Message);
|
||||||
|
var notification = new NotificationModel
|
||||||
//await NotificationService.Publish(new NotificationModel
|
{
|
||||||
//{
|
DateTime = DateTime.Now,
|
||||||
// DateTime = DateTime.Now,
|
User = Username,
|
||||||
// User = Username,
|
RequestType = RequestType.Movie,
|
||||||
// RequestType = RequestType.Movie,
|
Title = model.Title,
|
||||||
// Title = model.Title,
|
NotificationType = NotificationType.ItemAddedToFaultQueue
|
||||||
// NotificationType = NotificationType.ItemAddedToFaultQueue
|
};
|
||||||
//});
|
BackgroundJob.Enqueue(() => NotificationService.Publish(notification).Wait());
|
||||||
|
|
||||||
//return Response.AsJson(new JsonResponseModel
|
//return Response.AsJson(new JsonResponseModel
|
||||||
//{
|
//{
|
||||||
|
@ -160,16 +165,17 @@ namespace Ombi.Core.Engine
|
||||||
|
|
||||||
if (ShouldSendNotification(model.Type))
|
if (ShouldSendNotification(model.Type))
|
||||||
{
|
{
|
||||||
// var notificationModel = new NotificationModel
|
var notificationModel = new NotificationModel
|
||||||
// {
|
{
|
||||||
// Title = model.Title,
|
Title = model.Title,
|
||||||
// User = Username,
|
User = Username,
|
||||||
// DateTime = DateTime.Now,
|
DateTime = DateTime.Now,
|
||||||
// NotificationType = NotificationType.NewRequest,
|
NotificationType = NotificationType.NewRequest,
|
||||||
// RequestType = model.Type,
|
RequestType = model.Type,
|
||||||
// ImgSrc = model.Type == RequestType.Movie ? $"https://image.tmdb.org/t/p/w300/{model.PosterPath}" : model.PosterPath
|
ImgSrc = model.Type == RequestType.Movie ? $"https://image.tmdb.org/t/p/w300/{model.PosterPath}" : model.PosterPath
|
||||||
// };
|
};
|
||||||
// await NotificationService.Publish(notificationModel);
|
|
||||||
|
BackgroundJob.Enqueue(() => NotificationService.Publish(notificationModel).Wait());
|
||||||
}
|
}
|
||||||
|
|
||||||
//var limit = await RequestLimitRepo.GetAllAsync();
|
//var limit = await RequestLimitRepo.GetAllAsync();
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ombi.Api.TvMaze\Ombi.Api.TvMaze.csproj" />
|
<ProjectReference Include="..\Ombi.Api.TvMaze\Ombi.Api.TvMaze.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
|
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
|
||||||
|
<ProjectReference Include="..\Ombi.Notifications\Ombi.Notifications.csproj" />
|
||||||
|
<ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Store\Ombi.Store.csproj" />
|
<ProjectReference Include="..\Ombi.Store\Ombi.Store.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.TheMovieDbApi\Ombi.Api.TheMovieDb.csproj" />
|
<ProjectReference Include="..\Ombi.TheMovieDbApi\Ombi.Api.TheMovieDb.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -17,7 +17,10 @@ using Ombi.Core.IdentityResolver;
|
||||||
using Ombi.Core.Models.Requests;
|
using Ombi.Core.Models.Requests;
|
||||||
using Ombi.Core.Requests.Models;
|
using Ombi.Core.Requests.Models;
|
||||||
using Ombi.Core.Settings;
|
using Ombi.Core.Settings;
|
||||||
|
using Ombi.Notifications;
|
||||||
using Ombi.Schedule;
|
using Ombi.Schedule;
|
||||||
|
using Ombi.Schedule.Jobs;
|
||||||
|
using Ombi.Settings.Settings;
|
||||||
using Ombi.Store.Context;
|
using Ombi.Store.Context;
|
||||||
using Ombi.Store.Repository;
|
using Ombi.Store.Repository;
|
||||||
using Ombi.TheMovieDbApi;
|
using Ombi.TheMovieDbApi;
|
||||||
|
@ -71,13 +74,15 @@ namespace Ombi.DependencyInjection
|
||||||
public static IServiceCollection RegisterServices(this IServiceCollection services)
|
public static IServiceCollection RegisterServices(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddTransient<IRequestService, JsonRequestService>();
|
services.AddTransient<IRequestService, JsonRequestService>();
|
||||||
|
services.AddSingleton<INotificationService, NotificationService>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IServiceCollection RegisterJobs(this IServiceCollection services)
|
public static IServiceCollection RegisterJobs(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddTransient<ITestJob, TestJob>();
|
services.AddTransient<IPlexContentCacher, PlexContentCacher>();
|
||||||
|
services.AddTransient<IJobSetup, JobSetup>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,9 @@
|
||||||
<ProjectReference Include="..\Ombi.Api.Sonarr\Ombi.Api.Sonarr.csproj" />
|
<ProjectReference Include="..\Ombi.Api.Sonarr\Ombi.Api.Sonarr.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Api.TvMaze\Ombi.Api.TvMaze.csproj" />
|
<ProjectReference Include="..\Ombi.Api.TvMaze\Ombi.Api.TvMaze.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Core\Ombi.Core.csproj" />
|
<ProjectReference Include="..\Ombi.Core\Ombi.Core.csproj" />
|
||||||
|
<ProjectReference Include="..\Ombi.Notifications\Ombi.Notifications.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Schedule\Ombi.Schedule.csproj" />
|
<ProjectReference Include="..\Ombi.Schedule\Ombi.Schedule.csproj" />
|
||||||
|
<ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
|
@ -10,10 +10,4 @@
|
||||||
<PackageReference Include="System.Security.Claims" Version="4.3.0" />
|
<PackageReference Include="System.Security.Claims" Version="4.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="System.IdentityModel">
|
|
||||||
<HintPath>..\..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\System.IdentityModel.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
223
Ombi/Ombi.Notifications.Email/EmailNotification.cs
Normal file
223
Ombi/Ombi.Notifications.Email/EmailNotification.cs
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MailKit.Net.Smtp;
|
||||||
|
using MimeKit;
|
||||||
|
using Ombi.Core.Models.Requests;
|
||||||
|
using Ombi.Core.Settings;
|
||||||
|
using Ombi.Core.Settings.Models.Notifications;
|
||||||
|
using Ombi.Notifications.Models;
|
||||||
|
using Ombi.Notifications.Templates;
|
||||||
|
|
||||||
|
namespace Ombi.Notifications.Email
|
||||||
|
{
|
||||||
|
public class EmailNotification : BaseNotification<EmailNotificationSettings>
|
||||||
|
{
|
||||||
|
public EmailNotification(ISettingsService<EmailNotificationSettings> settings) : base(settings)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string NotificationName => nameof(EmailNotification);
|
||||||
|
|
||||||
|
protected override bool ValidateConfiguration(EmailNotificationSettings settings)
|
||||||
|
{
|
||||||
|
if (settings.Authentication)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(settings.EmailUsername) || string.IsNullOrEmpty(settings.EmailPassword))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(settings.EmailHost) || string.IsNullOrEmpty(settings.RecipientEmail) || string.IsNullOrEmpty(settings.EmailPort.ToString()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task NewRequest(NotificationModel model, EmailNotificationSettings settings)
|
||||||
|
{
|
||||||
|
var email = new EmailBasicTemplate();
|
||||||
|
var html = email.LoadTemplate(
|
||||||
|
$"Ombi: New {model.RequestType.GetString()?.ToLower()} request for {model.Title}!",
|
||||||
|
$"Hello! The user '{model.User}' has requested the {model.RequestType.GetString()?.ToLower()} '{model.Title}'! Please log in to approve this request. Request Date: {model.DateTime:f}",
|
||||||
|
model.ImgSrc);
|
||||||
|
|
||||||
|
|
||||||
|
var message = new NotificationMessage
|
||||||
|
{
|
||||||
|
Message = html,
|
||||||
|
Subject = $"Ombi: New {model.RequestType.GetString()?.ToLower()} request for {model.Title}!",
|
||||||
|
To = settings.RecipientEmail,
|
||||||
|
};
|
||||||
|
|
||||||
|
message.Other.Add("PlainTextBody", $"Hello! The user '{model.User}' has requested the {model.RequestType.GetString()?.ToLower()} '{model.Title}'! Please log in to approve this request. Request Date: {model.DateTime:f}");
|
||||||
|
|
||||||
|
await Send(message, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task Issue(NotificationModel model, EmailNotificationSettings settings)
|
||||||
|
{
|
||||||
|
var email = new EmailBasicTemplate();
|
||||||
|
var html = email.LoadTemplate(
|
||||||
|
$"Ombi: New issue for {model.Title}!",
|
||||||
|
$"Hello! The user '{model.User}' has reported a new issue {model.Body} for the title {model.Title}!",
|
||||||
|
model.ImgSrc);
|
||||||
|
|
||||||
|
var message = new NotificationMessage
|
||||||
|
{
|
||||||
|
Message = html,
|
||||||
|
Subject = $"Ombi: New issue for {model.Title}!",
|
||||||
|
To = settings.RecipientEmail,
|
||||||
|
};
|
||||||
|
|
||||||
|
message.Other.Add("PlainTextBody", $"Hello! The user '{model.User}' has reported a new issue {model.Body} for the title {model.Title}!");
|
||||||
|
|
||||||
|
await Send(message, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task AddedToRequestQueue(NotificationModel model, EmailNotificationSettings settings)
|
||||||
|
{
|
||||||
|
var email = new EmailBasicTemplate();
|
||||||
|
var html = email.LoadTemplate(
|
||||||
|
"Ombi: A request could not be added.",
|
||||||
|
$"Hello! The user '{model.User}' has requested {model.Title} but it could not be added. This has been added into the requests queue and will keep retrying",
|
||||||
|
model.ImgSrc);
|
||||||
|
|
||||||
|
var message = new NotificationMessage
|
||||||
|
{
|
||||||
|
Message = html,
|
||||||
|
Subject = $"Ombi: A request could not be added",
|
||||||
|
To = settings.RecipientEmail,
|
||||||
|
};
|
||||||
|
|
||||||
|
message.Other.Add("PlainTextBody", $"Hello! The user '{model.User}' has requested {model.Title} but it could not be added. This has been added into the requests queue and will keep retrying");
|
||||||
|
|
||||||
|
|
||||||
|
await Send(message, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task RequestDeclined(NotificationModel model, EmailNotificationSettings settings)
|
||||||
|
{
|
||||||
|
var email = new EmailBasicTemplate();
|
||||||
|
var html = email.LoadTemplate(
|
||||||
|
"Ombi: Your request has been declined",
|
||||||
|
$"Hello! Your request for {model.Title} has been declined, Sorry!",
|
||||||
|
model.ImgSrc);
|
||||||
|
|
||||||
|
var message = new NotificationMessage
|
||||||
|
{
|
||||||
|
Message = html,
|
||||||
|
Subject = $"Ombi: Your request has been declined",
|
||||||
|
To = model.UserEmail,
|
||||||
|
};
|
||||||
|
|
||||||
|
message.Other.Add("PlainTextBody", $"Hello! Your request for {model.Title} has been declined, Sorry!");
|
||||||
|
|
||||||
|
|
||||||
|
await Send(message, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task RequestApproved(NotificationModel model, EmailNotificationSettings settings)
|
||||||
|
{
|
||||||
|
var email = new EmailBasicTemplate();
|
||||||
|
var html = email.LoadTemplate(
|
||||||
|
"Ombi: Your request has been approved!",
|
||||||
|
$"Hello! Your request for {model.Title} has been approved!",
|
||||||
|
model.ImgSrc);
|
||||||
|
|
||||||
|
var message = new NotificationMessage
|
||||||
|
{
|
||||||
|
Message = html,
|
||||||
|
Subject = $"Ombi: Your request has been approved!",
|
||||||
|
To = model.UserEmail,
|
||||||
|
};
|
||||||
|
|
||||||
|
message.Other.Add("PlainTextBody", $"Hello! Your request for {model.Title} has been approved!");
|
||||||
|
|
||||||
|
await Send(message, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task AvailableRequest(NotificationModel model, EmailNotificationSettings settings)
|
||||||
|
{
|
||||||
|
var email = new EmailBasicTemplate();
|
||||||
|
var html = email.LoadTemplate(
|
||||||
|
$"Ombi: {model.Title} is now available!",
|
||||||
|
$"Hello! You requested {model.Title} on Ombi! This is now available on Plex! :)",
|
||||||
|
model.ImgSrc);
|
||||||
|
|
||||||
|
|
||||||
|
var message = new NotificationMessage
|
||||||
|
{
|
||||||
|
Message = html,
|
||||||
|
Subject = $"Ombi: {model.Title} is now available!",
|
||||||
|
To = model.UserEmail,
|
||||||
|
};
|
||||||
|
|
||||||
|
message.Other.Add("PlainTextBody", $"Hello! You requested {model.Title} on Ombi! This is now available on Plex! :)");
|
||||||
|
|
||||||
|
await Send(message, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task Send(NotificationMessage model, EmailNotificationSettings settings)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var body = new BodyBuilder
|
||||||
|
{
|
||||||
|
HtmlBody = model.Message,
|
||||||
|
TextBody = model.Other["PlainTextBody"]
|
||||||
|
};
|
||||||
|
|
||||||
|
var message = new MimeMessage
|
||||||
|
{
|
||||||
|
Body = body.ToMessageBody(),
|
||||||
|
Subject = model.Subject
|
||||||
|
};
|
||||||
|
message.From.Add(new MailboxAddress(settings.EmailSender, settings.EmailSender));
|
||||||
|
message.To.Add(new MailboxAddress(model.To, model.To));
|
||||||
|
|
||||||
|
using (var client = new SmtpClient())
|
||||||
|
{
|
||||||
|
client.Connect(settings.EmailHost, settings.EmailPort); // Let MailKit figure out the correct SecureSocketOptions.
|
||||||
|
|
||||||
|
// Note: since we don't have an OAuth2 token, disable
|
||||||
|
// the XOAUTH2 authentication mechanism.
|
||||||
|
client.AuthenticationMechanisms.Remove("XOAUTH2");
|
||||||
|
|
||||||
|
if (settings.Authentication)
|
||||||
|
{
|
||||||
|
client.Authenticate(settings.EmailUsername, settings.EmailPassword);
|
||||||
|
}
|
||||||
|
//Log.Info("sending message to {0} \r\n from: {1}\r\n Are we authenticated: {2}", message.To, message.From, client.IsAuthenticated);
|
||||||
|
await client.SendAsync(message);
|
||||||
|
await client.DisconnectAsync(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
//Log.Error(e);
|
||||||
|
throw new InvalidOperationException(e.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task Test(NotificationModel model, EmailNotificationSettings settings)
|
||||||
|
{
|
||||||
|
var email = new EmailBasicTemplate();
|
||||||
|
var html = email.LoadTemplate(
|
||||||
|
"Test Message",
|
||||||
|
"This is just a test! Success!",
|
||||||
|
model.ImgSrc);
|
||||||
|
var message = new NotificationMessage
|
||||||
|
{
|
||||||
|
Message = html,
|
||||||
|
Subject = $"Ombi: Test",
|
||||||
|
To = settings.RecipientEmail,
|
||||||
|
};
|
||||||
|
|
||||||
|
message.Other.Add("PlainTextBody", "This is just a test! Success!");
|
||||||
|
|
||||||
|
await Send(message, settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard1.6</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="MailKit" Version="1.16.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ombi.Core\Ombi.Core.csproj" />
|
||||||
|
<ProjectReference Include="..\Ombi.Notifications.Templates\Ombi.Notifications.Templates.csproj" />
|
||||||
|
<ProjectReference Include="..\Ombi.Notifications\Ombi.Notifications.csproj" />
|
||||||
|
<ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
8
Ombi/Ombi.Notifications.Emil/Class1.cs
Normal file
8
Ombi/Ombi.Notifications.Emil/Class1.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ombi.Notifications.Emil
|
||||||
|
{
|
||||||
|
public class Class1
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard1.4</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
34
Ombi/Ombi.Notifications.Templates/EmailBasicTemplate.cs
Normal file
34
Ombi/Ombi.Notifications.Templates/EmailBasicTemplate.cs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Ombi.Notifications.Templates
|
||||||
|
{
|
||||||
|
public class EmailBasicTemplate : IEmailBasicTemplate
|
||||||
|
{
|
||||||
|
public string TemplateLocation => Path.Combine(Directory.GetCurrentDirectory(), "Templates","BasicTemplate.html");
|
||||||
|
|
||||||
|
private const string SubjectKey = "{@SUBJECT}";
|
||||||
|
private const string BodyKey = "{@BODY}";
|
||||||
|
private const string ImgSrc = "{@IMGSRC}";
|
||||||
|
private const string DateKey = "{@DATENOW}";
|
||||||
|
|
||||||
|
public string LoadTemplate(string subject, string body, string imgSrc)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder(File.ReadAllText(TemplateLocation));
|
||||||
|
sb.Replace(SubjectKey, subject);
|
||||||
|
sb.Replace(BodyKey, body);
|
||||||
|
sb.Replace(ImgSrc, imgSrc);
|
||||||
|
sb.Replace(DateKey, DateTime.Now.ToString("f"));
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
Ombi/Ombi.Notifications.Templates/IEmailBasicTemplate.cs
Normal file
8
Ombi/Ombi.Notifications.Templates/IEmailBasicTemplate.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Ombi.Notifications.Templates
|
||||||
|
{
|
||||||
|
public interface IEmailBasicTemplate
|
||||||
|
{
|
||||||
|
string LoadTemplate(string subject, string body, string imgSrc);
|
||||||
|
string TemplateLocation { get; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard1.4</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
189
Ombi/Ombi.Notifications.Templates/Templates/BasicTemplate.html
Normal file
189
Ombi/Ombi.Notifications.Templates/Templates/BasicTemplate.html
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width" />
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<title>Ombi</title>
|
||||||
|
<style media="all" type="text/css">
|
||||||
|
@media all {
|
||||||
|
.btn-primary table td:hover {
|
||||||
|
background-color: #34495e !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary a:hover {
|
||||||
|
background-color: #34495e !important;
|
||||||
|
border-color: #34495e !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all {
|
||||||
|
.btn-secondary a:hover {
|
||||||
|
border-color: #34495e !important;
|
||||||
|
color: #34495e !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 620px) {
|
||||||
|
table[class=body] h1 {
|
||||||
|
font-size: 28px !important;
|
||||||
|
margin-bottom: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] h2 {
|
||||||
|
font-size: 22px !important;
|
||||||
|
margin-bottom: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] h3 {
|
||||||
|
font-size: 16px !important;
|
||||||
|
margin-bottom: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] p,
|
||||||
|
table[class=body] ul,
|
||||||
|
table[class=body] ol,
|
||||||
|
table[class=body] td,
|
||||||
|
table[class=body] span,
|
||||||
|
table[class=body] a {
|
||||||
|
font-size: 16px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .wrapper,
|
||||||
|
table[class=body] .article {
|
||||||
|
padding: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .content {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .container {
|
||||||
|
padding: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .header {
|
||||||
|
margin-bottom: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .main {
|
||||||
|
border-left-width: 0 !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
border-right-width: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .btn table {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .btn a {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .img-responsive {
|
||||||
|
height: auto !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
width: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .alert td {
|
||||||
|
border-radius: 0 !important;
|
||||||
|
padding: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .span-2,
|
||||||
|
table[class=body] .span-3 {
|
||||||
|
max-width: none !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .receipt {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all {
|
||||||
|
.ExternalClass {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ExternalClass,
|
||||||
|
.ExternalClass p,
|
||||||
|
.ExternalClass span,
|
||||||
|
.ExternalClass font,
|
||||||
|
.ExternalClass td,
|
||||||
|
.ExternalClass div {
|
||||||
|
line-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apple-link a {
|
||||||
|
color: inherit !important;
|
||||||
|
font-family: inherit !important;
|
||||||
|
font-size: inherit !important;
|
||||||
|
font-weight: inherit !important;
|
||||||
|
line-height: inherit !important;
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="" style="font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; background-color: #f6f6f6; margin: 0; padding: 0;">
|
||||||
|
<table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background-color: #f6f6f6;" width="100%" bgcolor="#f6f6f6">
|
||||||
|
<tr>
|
||||||
|
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top"> </td>
|
||||||
|
<td class="container" style="font-family: sans-serif; font-size: 14px; vertical-align: top; display: block; Margin: 0 auto !important; max-width: 580px; padding: 10px; width: 580px;" width="580" valign="top">
|
||||||
|
<div class="content" style="box-sizing: border-box; display: block; Margin: 0 auto; max-width: 580px; padding: 10px;">
|
||||||
|
|
||||||
|
<!-- START CENTERED WHITE CONTAINER -->
|
||||||
|
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">Ombi</span>
|
||||||
|
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #fff; border-radius: 3px;" width="100%">
|
||||||
|
|
||||||
|
<!-- START MAIN CONTENT AREA -->
|
||||||
|
<tr>
|
||||||
|
<td class="wrapper" style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;" valign="top">
|
||||||
|
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<img src="http://i.imgur.com/qQsN78U.png" width="400px" text-align="center" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">
|
||||||
|
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">Hi there!</p>
|
||||||
|
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">{@SUBJECT}</p>
|
||||||
|
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">{@BODY}</p>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<img src="{@IMGSRC}" width="400px" text-align="center" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- END MAIN CONTENT AREA -->
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- START FOOTER -->
|
||||||
|
<div class="footer" style="clear: both; padding-top: 10px; text-align: center; width: 100%;">
|
||||||
|
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
|
||||||
|
<tr>
|
||||||
|
<td class="content-block powered-by" style="font-family: sans-serif; vertical-align: top; padding-top: 10px; padding-bottom: 10px; font-size: 12px; color: #999999; text-align: center;" valign="top" align="center">
|
||||||
|
Powered by <a href="https://github.com/tidusjar/Ombi" style="color: #999999; font-size: 12px; text-align: center; text-decoration: underline;">Ombi</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- END FOOTER -->
|
||||||
|
<!-- END CENTERED WHITE CONTAINER -->
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top"> </td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
91
Ombi/Ombi.Notifications/BaseNotification.cs
Normal file
91
Ombi/Ombi.Notifications/BaseNotification.cs
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ombi.Core.Settings;
|
||||||
|
using Ombi.Core.Settings.Models;
|
||||||
|
using Ombi.Notifications.Models;
|
||||||
|
|
||||||
|
namespace Ombi.Notifications
|
||||||
|
{
|
||||||
|
public abstract class BaseNotification<T> : INotification where T : Settings.Settings.Models.Settings, new()
|
||||||
|
{
|
||||||
|
protected BaseNotification(ISettingsService<T> settings)
|
||||||
|
{
|
||||||
|
Settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ISettingsService<T> Settings { get; }
|
||||||
|
public abstract string NotificationName { get; }
|
||||||
|
|
||||||
|
public async Task NotifyAsync(NotificationModel model)
|
||||||
|
{
|
||||||
|
var configuration = GetConfiguration();
|
||||||
|
await NotifyAsync(model, configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task NotifyAsync(NotificationModel model, Settings.Settings.Models.Settings settings)
|
||||||
|
{
|
||||||
|
if (settings == null) await NotifyAsync(model);
|
||||||
|
|
||||||
|
var notificationSettings = (T)settings;
|
||||||
|
|
||||||
|
if (!ValidateConfiguration(notificationSettings))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
switch (model.NotificationType)
|
||||||
|
{
|
||||||
|
case NotificationType.NewRequest:
|
||||||
|
await NewRequest(model, notificationSettings);
|
||||||
|
break;
|
||||||
|
case NotificationType.Issue:
|
||||||
|
await Issue(model, notificationSettings);
|
||||||
|
break;
|
||||||
|
case NotificationType.RequestAvailable:
|
||||||
|
await AvailableRequest(model, notificationSettings);
|
||||||
|
break;
|
||||||
|
case NotificationType.RequestApproved:
|
||||||
|
await RequestApproved(model, notificationSettings);
|
||||||
|
break;
|
||||||
|
case NotificationType.AdminNote:
|
||||||
|
throw new NotImplementedException();
|
||||||
|
|
||||||
|
case NotificationType.Test:
|
||||||
|
await Test(model, notificationSettings);
|
||||||
|
break;
|
||||||
|
case NotificationType.RequestDeclined:
|
||||||
|
await RequestDeclined(model, notificationSettings);
|
||||||
|
break;
|
||||||
|
case NotificationType.ItemAddedToFaultQueue:
|
||||||
|
await AddedToRequestQueue(model, notificationSettings);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (NotImplementedException)
|
||||||
|
{
|
||||||
|
// Do nothing, it's not implimented meaning it might not be ready or even used
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private T GetConfiguration()
|
||||||
|
{
|
||||||
|
var settings = Settings.GetSettings();
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected abstract bool ValidateConfiguration(T settings);
|
||||||
|
protected abstract Task NewRequest(NotificationModel model, T settings);
|
||||||
|
protected abstract Task Issue(NotificationModel model, T settings);
|
||||||
|
protected abstract Task AddedToRequestQueue(NotificationModel model, T settings);
|
||||||
|
protected abstract Task RequestDeclined(NotificationModel model, T settings);
|
||||||
|
protected abstract Task RequestApproved(NotificationModel model, T settings);
|
||||||
|
protected abstract Task AvailableRequest(NotificationModel model, T settings);
|
||||||
|
protected abstract Task Send(NotificationMessage model, T settings);
|
||||||
|
protected abstract Task Test(NotificationModel model, T settings);
|
||||||
|
}
|
||||||
|
}
|
21
Ombi/Ombi.Notifications/Interfaces/INotification.cs
Normal file
21
Ombi/Ombi.Notifications/Interfaces/INotification.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ombi.Core.Settings.Models;
|
||||||
|
using Ombi.Notifications.Models;
|
||||||
|
|
||||||
|
namespace Ombi.Notifications
|
||||||
|
{
|
||||||
|
public interface INotification
|
||||||
|
{
|
||||||
|
string NotificationName { get; }
|
||||||
|
|
||||||
|
Task NotifyAsync(NotificationModel model);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a notification to the user, this is usually for testing the settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model.</param>
|
||||||
|
/// <param name="settings">The settings.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task NotifyAsync(NotificationModel model, Settings.Settings.Models.Settings settings);
|
||||||
|
}
|
||||||
|
}
|
18
Ombi/Ombi.Notifications/Interfaces/INotificationService.cs
Normal file
18
Ombi/Ombi.Notifications/Interfaces/INotificationService.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ombi.Core.Settings.Models;
|
||||||
|
using Ombi.Notifications.Models;
|
||||||
|
|
||||||
|
namespace Ombi.Notifications
|
||||||
|
{
|
||||||
|
public interface INotificationService
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<string, INotification> Observers { get; }
|
||||||
|
|
||||||
|
Task Publish(NotificationModel model);
|
||||||
|
Task Publish(NotificationModel model, Settings.Settings.Models.Settings settings);
|
||||||
|
Task PublishTest(NotificationModel model, Settings.Settings.Models.Settings settings, INotification type);
|
||||||
|
void Subscribe(INotification notification);
|
||||||
|
void UnSubscribe(INotification notification);
|
||||||
|
}
|
||||||
|
}
|
13
Ombi/Ombi.Notifications/Models/NotificationMessage.cs
Normal file
13
Ombi/Ombi.Notifications/Models/NotificationMessage.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ombi.Notifications.Models
|
||||||
|
{
|
||||||
|
public class NotificationMessage
|
||||||
|
{
|
||||||
|
public string Subject { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
public string To { get; set; }
|
||||||
|
|
||||||
|
public Dictionary<string, string> Other { get; set; } = new Dictionary<string, string>();
|
||||||
|
}
|
||||||
|
}
|
17
Ombi/Ombi.Notifications/Models/NotificationModel.cs
Normal file
17
Ombi/Ombi.Notifications/Models/NotificationModel.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
using System;
|
||||||
|
using Ombi.Store.Entities;
|
||||||
|
|
||||||
|
namespace Ombi.Notifications.Models
|
||||||
|
{
|
||||||
|
public class NotificationModel
|
||||||
|
{
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string Body { get; set; }
|
||||||
|
public DateTime DateTime { get; set; }
|
||||||
|
public NotificationType NotificationType { get; set; }
|
||||||
|
public string User { get; set; }
|
||||||
|
public string UserEmail { get; set; }
|
||||||
|
public RequestType RequestType { get; set; }
|
||||||
|
public string ImgSrc { get; set; }
|
||||||
|
}
|
||||||
|
}
|
78
Ombi/Ombi.Notifications/NotificationService.cs
Normal file
78
Ombi/Ombi.Notifications/NotificationService.cs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ombi.Core.Settings.Models;
|
||||||
|
using Ombi.Notifications.Models;
|
||||||
|
|
||||||
|
namespace Ombi.Notifications
|
||||||
|
{
|
||||||
|
public class NotificationService : INotificationService
|
||||||
|
{
|
||||||
|
public ConcurrentDictionary<string, INotification> Observers { get; } = new ConcurrentDictionary<string, INotification>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a notification to the user. This one is used in normal notification scenarios
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task Publish(NotificationModel model)
|
||||||
|
{
|
||||||
|
var notificationTasks = Observers.Values.Select(notification => NotifyAsync(notification, model));
|
||||||
|
|
||||||
|
await Task.WhenAll(notificationTasks).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a notification to the user, this is usually for testing the settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model.</param>
|
||||||
|
/// <param name="settings">The settings.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task Publish(NotificationModel model, Settings.Settings.Models.Settings settings)
|
||||||
|
{
|
||||||
|
var notificationTasks = Observers.Values.Select(notification => NotifyAsync(notification, model, settings));
|
||||||
|
|
||||||
|
await Task.WhenAll(notificationTasks).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Subscribe(INotification notification)
|
||||||
|
{
|
||||||
|
Observers.TryAdd(notification.NotificationName, notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnSubscribe(INotification notification)
|
||||||
|
{
|
||||||
|
Observers.TryRemove(notification.NotificationName, out notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task NotifyAsync(INotification notification, NotificationModel model)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await notification.NotifyAsync(model).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task NotifyAsync(INotification notification, NotificationModel model, Settings.Settings.Models.Settings settings)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await notification.NotifyAsync(model, settings).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task PublishTest(NotificationModel model, Settings.Settings.Models.Settings settings, INotification type)
|
||||||
|
{
|
||||||
|
await type.NotifyAsync(model, settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
Ombi/Ombi.Notifications/NotificationType.cs
Normal file
16
Ombi/Ombi.Notifications/NotificationType.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ombi.Notifications
|
||||||
|
{
|
||||||
|
public enum NotificationType
|
||||||
|
{
|
||||||
|
NewRequest,
|
||||||
|
Issue,
|
||||||
|
RequestAvailable,
|
||||||
|
RequestApproved,
|
||||||
|
AdminNote,
|
||||||
|
Test,
|
||||||
|
RequestDeclined,
|
||||||
|
ItemAddedToFaultQueue
|
||||||
|
}
|
||||||
|
}
|
12
Ombi/Ombi.Notifications/Ombi.Notifications.csproj
Normal file
12
Ombi/Ombi.Notifications/Ombi.Notifications.csproj
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard1.6</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" />
|
||||||
|
<ProjectReference Include="..\Ombi.Store\Ombi.Store.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
7
Ombi/Ombi.Schedule/IJobSetup.cs
Normal file
7
Ombi/Ombi.Schedule/IJobSetup.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Ombi.Schedule
|
||||||
|
{
|
||||||
|
public interface IJobSetup
|
||||||
|
{
|
||||||
|
void Setup();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +0,0 @@
|
||||||
using Hangfire.RecurringJobExtensions;
|
|
||||||
using Hangfire.Server;
|
|
||||||
|
|
||||||
namespace Ombi.Schedule
|
|
||||||
{
|
|
||||||
public interface ITestJob
|
|
||||||
{
|
|
||||||
[RecurringJob("*/1 * * * *")]
|
|
||||||
void Test(PerformContext context);
|
|
||||||
}
|
|
||||||
}
|
|
20
Ombi/Ombi.Schedule/JobSetup.cs
Normal file
20
Ombi/Ombi.Schedule/JobSetup.cs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
using System;
|
||||||
|
using Hangfire;
|
||||||
|
using Ombi.Schedule.Jobs;
|
||||||
|
|
||||||
|
namespace Ombi.Schedule
|
||||||
|
{
|
||||||
|
public class JobSetup : IJobSetup
|
||||||
|
{
|
||||||
|
public JobSetup(IPlexContentCacher cacher)
|
||||||
|
{
|
||||||
|
Cacher = cacher;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IPlexContentCacher Cacher { get; }
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
RecurringJob.AddOrUpdate(() => Cacher.CacheContent(), Cron.Hourly, TimeZoneInfo.Utc, "Cacher");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
Ombi/Ombi.Schedule/Jobs/IPlexContentCacher.cs
Normal file
7
Ombi/Ombi.Schedule/Jobs/IPlexContentCacher.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Ombi.Schedule.Jobs
|
||||||
|
{
|
||||||
|
public interface IPlexContentCacher
|
||||||
|
{
|
||||||
|
void CacheContent();
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,11 +32,10 @@ using Ombi.Api.Plex;
|
||||||
using Ombi.Api.Plex.Models;
|
using Ombi.Api.Plex.Models;
|
||||||
using Ombi.Core.Settings;
|
using Ombi.Core.Settings;
|
||||||
using Ombi.Core.Settings.Models.External;
|
using Ombi.Core.Settings.Models.External;
|
||||||
using Ombi.Helpers;
|
|
||||||
|
|
||||||
namespace Ombi.Schedule.Jobs
|
namespace Ombi.Schedule.Jobs
|
||||||
{
|
{
|
||||||
public partial class PlexContentCacher
|
public partial class PlexContentCacher : IPlexContentCacher
|
||||||
{
|
{
|
||||||
public PlexContentCacher(ISettingsService<PlexSettings> plex, IPlexApi plexApi)
|
public PlexContentCacher(ISettingsService<PlexSettings> plex, IPlexApi plexApi)
|
||||||
{
|
{
|
||||||
|
@ -132,7 +131,7 @@ namespace Ombi.Schedule.Jobs
|
||||||
{
|
{
|
||||||
if (plex.Enable)
|
if (plex.Enable)
|
||||||
{
|
{
|
||||||
if (plex?.Ip == null || plex?.PlexAuthToken == null)
|
if (string.IsNullOrEmpty(plex?.Ip) || string.IsNullOrEmpty(plex?.PlexAuthToken))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ombi.Api.Plex\Ombi.Api.Plex.csproj" />
|
<ProjectReference Include="..\Ombi.Api.Plex\Ombi.Api.Plex.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Core\Ombi.Core.csproj" />
|
<ProjectReference Include="..\Ombi.Core\Ombi.Core.csproj" />
|
||||||
|
<ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
|
@ -1,17 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using Hangfire;
|
|
||||||
using Hangfire.RecurringJobExtensions;
|
|
||||||
using Hangfire.Server;
|
|
||||||
|
|
||||||
namespace Ombi.Schedule
|
|
||||||
{
|
|
||||||
public class TestJob : ITestJob
|
|
||||||
{
|
|
||||||
[RecurringJob("*/1 * * * *")]
|
|
||||||
public void Test(PerformContext context)
|
|
||||||
{
|
|
||||||
Debug.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss} TestJob1 Running ...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
17
Ombi/Ombi.Settings/Ombi.Settings.csproj
Normal file
17
Ombi/Ombi.Settings/Ombi.Settings.csproj
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard1.6</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="1.1.0" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
|
||||||
|
<ProjectReference Include="..\Ombi.Store\Ombi.Store.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -4,7 +4,7 @@ using Ombi.Helpers;
|
||||||
|
|
||||||
namespace Ombi.Core.Settings.Models.External
|
namespace Ombi.Core.Settings.Models.External
|
||||||
{
|
{
|
||||||
public abstract class ExternalSettings : Settings
|
public abstract class ExternalSettings : Ombi.Settings.Settings.Models.Settings
|
||||||
{
|
{
|
||||||
public bool Ssl { get; set; }
|
public bool Ssl { get; set; }
|
||||||
public string SubDir { get; set; }
|
public string SubDir { get; set; }
|
|
@ -3,7 +3,7 @@ using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Ombi.Core.Settings.Models
|
namespace Ombi.Core.Settings.Models
|
||||||
{
|
{
|
||||||
public class LandingPageSettings : Settings
|
public class LandingPageSettings : Ombi.Settings.Settings.Models.Settings
|
||||||
{
|
{
|
||||||
public bool Enabled { get; set; }
|
public bool Enabled { get; set; }
|
||||||
public bool BeforeLogin { get; set; }
|
public bool BeforeLogin { get; set; }
|
|
@ -0,0 +1,14 @@
|
||||||
|
namespace Ombi.Core.Settings.Models.Notifications
|
||||||
|
{
|
||||||
|
public sealed class EmailNotificationSettings : Ombi.Settings.Settings.Models.Settings
|
||||||
|
{
|
||||||
|
public bool Enabled { get; set; }
|
||||||
|
public string EmailHost { get; set; }
|
||||||
|
public string EmailPassword { get; set; }
|
||||||
|
public int EmailPort { get; set; }
|
||||||
|
public string EmailSender { get; set; }
|
||||||
|
public string EmailUsername { get; set; }
|
||||||
|
public bool Authentication { get; set; }
|
||||||
|
public string RecipientEmail { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Ombi.Core.Settings.Models
|
namespace Ombi.Core.Settings.Models
|
||||||
{
|
{
|
||||||
public class OmbiSettings : Settings
|
public class OmbiSettings : Ombi.Settings.Settings.Models.Settings
|
||||||
{
|
{
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
//public string BaseUrl { get; set; }
|
//public string BaseUrl { get; set; }
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ombi.Core.Settings.Models
|
namespace Ombi.Settings.Settings.Models
|
||||||
{
|
{
|
||||||
public class Settings
|
public class Settings
|
||||||
{
|
{
|
|
@ -1,13 +1,14 @@
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Ombi.Core.Settings;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
using Ombi.Store.Repository;
|
using Ombi.Store.Repository;
|
||||||
|
|
||||||
namespace Ombi.Core.Settings
|
namespace Ombi.Settings.Settings
|
||||||
{
|
{
|
||||||
public class SettingsServiceV2<T> : ISettingsService<T>
|
public class SettingsServiceV2<T> : ISettingsService<T>
|
||||||
where T : Models.Settings, new()
|
where T : Ombi.Settings.Settings.Models.Settings, new()
|
||||||
{
|
{
|
||||||
|
|
||||||
public SettingsServiceV2(ISettingsRepository repo)
|
public SettingsServiceV2(ISettingsRepository repo)
|
|
@ -2,6 +2,7 @@
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
|
|
||||||
namespace Ombi.Store.Context
|
namespace Ombi.Store.Context
|
||||||
|
@ -13,5 +14,6 @@ namespace Ombi.Store.Context
|
||||||
DbSet<RequestBlobs> Requests { get; set; }
|
DbSet<RequestBlobs> Requests { get; set; }
|
||||||
DbSet<GlobalSettings> Settings { get; set; }
|
DbSet<GlobalSettings> Settings { get; set; }
|
||||||
DbSet<User> Users { get; set; }
|
DbSet<User> Users { get; set; }
|
||||||
|
EntityEntry<GlobalSettings> Entry(GlobalSettings settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
|
|
||||||
namespace Ombi.Store.Context
|
namespace Ombi.Store.Context
|
||||||
|
@ -13,10 +14,16 @@ namespace Ombi.Store.Context
|
||||||
_created = true;
|
_created = true;
|
||||||
Database.EnsureCreated();
|
Database.EnsureCreated();
|
||||||
Database.Migrate();
|
Database.Migrate();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
public DbSet<RequestBlobs> Requests { get; set; }
|
public DbSet<RequestBlobs> Requests { get; set; }
|
||||||
public DbSet<GlobalSettings> Settings { get; set; }
|
public DbSet<GlobalSettings> Settings { get; set; }
|
||||||
public DbSet<User> Users { get; set; }
|
public DbSet<User> Users { get; set; }
|
||||||
|
public EntityEntry<GlobalSettings> Entry(GlobalSettings settings)
|
||||||
|
{
|
||||||
|
return Entry(settings);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
{
|
||||||
|
|
|
@ -76,6 +76,7 @@ namespace Ombi.Store.Repository
|
||||||
|
|
||||||
public void Update(GlobalSettings entity)
|
public void Update(GlobalSettings entity)
|
||||||
{
|
{
|
||||||
|
Db.Entry(entity).State = EntityState.Modified;
|
||||||
Db.SaveChanges();
|
Db.SaveChanges();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
24
Ombi/Ombi.Tests.Core/Ombi.Tests.Core.csproj
Normal file
24
Ombi/Ombi.Tests.Core/Ombi.Tests.Core.csproj
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp1.1</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AutoMapper" Version="6.0.2" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
|
||||||
|
<PackageReference Include="Moq" Version="4.7.8" />
|
||||||
|
<PackageReference Include="xunit" Version="2.2.0" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ombi.Core\Ombi.Core.csproj" />
|
||||||
|
<ProjectReference Include="..\Ombi.Store\Ombi.Store.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
32
Ombi/Ombi.Tests.Core/UserIdentityManagerTests.cs
Normal file
32
Ombi/Ombi.Tests.Core/UserIdentityManagerTests.cs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AutoMapper;
|
||||||
|
using Moq;
|
||||||
|
using Ombi.Core.IdentityResolver;
|
||||||
|
using Ombi.Core.Models;
|
||||||
|
using Ombi.Store.Entities;
|
||||||
|
using Ombi.Store.Repository;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ombi.Tests.Core
|
||||||
|
{
|
||||||
|
public class UserIdentityManagerTests
|
||||||
|
{
|
||||||
|
public UserIdentityManagerTests()
|
||||||
|
{
|
||||||
|
UserRepo = new Mock<IUserRepository>();
|
||||||
|
var mapper = new Mock<IMapper>();
|
||||||
|
Manager = new UserIdentityManager(UserRepo.Object, mapper.Object);
|
||||||
|
}
|
||||||
|
private Mock<IUserRepository> UserRepo { get; }
|
||||||
|
private IUserIdentityManager Manager { get; }
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CredentialsValid()
|
||||||
|
{
|
||||||
|
UserRepo.Setup(x => x.GetUser("ABC")).ReturnsAsync(() => new User{Password = "4tDKIbNCZ0pMxNzSSjVbT6mG88o52x9jOixPEwQS9rg=", Salt = new byte[]{30,80,214,127,185,134,75,86,80,177,0,242,202,161,219,246}});
|
||||||
|
|
||||||
|
Assert.True(await Manager.CredentialsValid("ABC", "ABC"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,6 +44,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{6F42AB98
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.TvMaze", "Ombi.Api.TvMaze\Ombi.Api.TvMaze.csproj", "{0E8EF835-E4F0-4EE5-A2B6-678DEE973721}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.TvMaze", "Ombi.Api.TvMaze\Ombi.Api.TvMaze.csproj", "{0E8EF835-E4F0-4EE5-A2B6-678DEE973721}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Notifications", "Ombi.Notifications\Ombi.Notifications.csproj", "{E6EE2830-E4AC-4F2E-AD93-2C9305605761}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Notifications", "Notifications", "{EA30DD15-6280-4687-B370-2956EC2E54E5}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Notifications.Email", "Ombi.Notifications.Email\Ombi.Notifications.Email.csproj", "{72DB97D7-2D60-4B96-8C57-6C0E20E892EB}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Notifications.Templates", "Ombi.Notifications.Templates\Ombi.Notifications.Templates.csproj", "{6EE01B17-0966-4E11-8BC1-A5318A92AB1D}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Tests.Core", "Ombi.Tests.Core\Ombi.Tests.Core.csproj", "{627A27A7-8879-4851-8140-38F8F5ADD6CD}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Settings", "Ombi.Settings\Ombi.Settings.csproj", "{AE3AA23D-5B66-42AF-B44E-B9B4D8856C6F}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -102,6 +114,26 @@ Global
|
||||||
{0E8EF835-E4F0-4EE5-A2B6-678DEE973721}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{0E8EF835-E4F0-4EE5-A2B6-678DEE973721}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{0E8EF835-E4F0-4EE5-A2B6-678DEE973721}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{0E8EF835-E4F0-4EE5-A2B6-678DEE973721}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{0E8EF835-E4F0-4EE5-A2B6-678DEE973721}.Release|Any CPU.Build.0 = Release|Any CPU
|
{0E8EF835-E4F0-4EE5-A2B6-678DEE973721}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{E6EE2830-E4AC-4F2E-AD93-2C9305605761}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{E6EE2830-E4AC-4F2E-AD93-2C9305605761}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{E6EE2830-E4AC-4F2E-AD93-2C9305605761}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{E6EE2830-E4AC-4F2E-AD93-2C9305605761}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{72DB97D7-2D60-4B96-8C57-6C0E20E892EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{72DB97D7-2D60-4B96-8C57-6C0E20E892EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{72DB97D7-2D60-4B96-8C57-6C0E20E892EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{72DB97D7-2D60-4B96-8C57-6C0E20E892EB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{6EE01B17-0966-4E11-8BC1-A5318A92AB1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{6EE01B17-0966-4E11-8BC1-A5318A92AB1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{6EE01B17-0966-4E11-8BC1-A5318A92AB1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{6EE01B17-0966-4E11-8BC1-A5318A92AB1D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{627A27A7-8879-4851-8140-38F8F5ADD6CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{627A27A7-8879-4851-8140-38F8F5ADD6CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{627A27A7-8879-4851-8140-38F8F5ADD6CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{627A27A7-8879-4851-8140-38F8F5ADD6CD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{AE3AA23D-5B66-42AF-B44E-B9B4D8856C6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{AE3AA23D-5B66-42AF-B44E-B9B4D8856C6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{AE3AA23D-5B66-42AF-B44E-B9B4D8856C6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{AE3AA23D-5B66-42AF-B44E-B9B4D8856C6F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -115,5 +147,9 @@ Global
|
||||||
{08FF107D-31E1-470D-AF86-E09B015CEE06} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
{08FF107D-31E1-470D-AF86-E09B015CEE06} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
||||||
{CFB5E008-D0D0-43C0-AA06-89E49D17F384} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
{CFB5E008-D0D0-43C0-AA06-89E49D17F384} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
||||||
{0E8EF835-E4F0-4EE5-A2B6-678DEE973721} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
{0E8EF835-E4F0-4EE5-A2B6-678DEE973721} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
||||||
|
{E6EE2830-E4AC-4F2E-AD93-2C9305605761} = {EA30DD15-6280-4687-B370-2956EC2E54E5}
|
||||||
|
{72DB97D7-2D60-4B96-8C57-6C0E20E892EB} = {EA30DD15-6280-4687-B370-2956EC2E54E5}
|
||||||
|
{6EE01B17-0966-4E11-8BC1-A5318A92AB1D} = {EA30DD15-6280-4687-B370-2956EC2E54E5}
|
||||||
|
{627A27A7-8879-4851-8140-38F8F5ADD6CD} = {6F42AB98-9196-44C4-B888-D5E409F415A1}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
@ -56,6 +56,7 @@ namespace Ombi.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("landingpage")]
|
[HttpGet("landingpage")]
|
||||||
|
[AllowAnonymous]
|
||||||
public async Task<LandingPageSettings> LandingPageSettings()
|
public async Task<LandingPageSettings> LandingPageSettings()
|
||||||
{
|
{
|
||||||
return await Get<LandingPageSettings>();
|
return await Get<LandingPageSettings>();
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
<ProjectReference Include="..\Ombi.DependencyInjection\Ombi.DependencyInjection.csproj" />
|
<ProjectReference Include="..\Ombi.DependencyInjection\Ombi.DependencyInjection.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Mapping\Ombi.Mapping.csproj" />
|
<ProjectReference Include="..\Ombi.Mapping\Ombi.Mapping.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Schedule\Ombi.Schedule.csproj" />
|
<ProjectReference Include="..\Ombi.Schedule\Ombi.Schedule.csproj" />
|
||||||
|
<ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.TheMovieDbApi\Ombi.Api.TheMovieDb.csproj" />
|
<ProjectReference Include="..\Ombi.TheMovieDbApi\Ombi.Api.TheMovieDb.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -57,6 +58,9 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Content Update="wwwroot\app\landingpage\landingpage.component.html">
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
<Content Update="wwwroot\app\services\request - Copy.service.js.map">
|
<Content Update="wwwroot\app\services\request - Copy.service.js.map">
|
||||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
|
@ -47,17 +47,15 @@ namespace Ombi
|
||||||
services.RegisterDependencies(); // Ioc and EF
|
services.RegisterDependencies(); // Ioc and EF
|
||||||
|
|
||||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||||
//services.AddTransient<IPrincipal>(new InjectionFactory(u => HttpContext.Current.User));
|
|
||||||
services.AddScoped<IPrincipal>(sp => sp.GetService<IHttpContextAccessor>().HttpContext.User);
|
services.AddScoped<IPrincipal>(sp => sp.GetService<IHttpContextAccessor>().HttpContext.User);
|
||||||
|
|
||||||
|
|
||||||
services.AddHangfire(x =>
|
services.AddHangfire(x =>
|
||||||
{
|
{
|
||||||
x.UseMemoryStorage(new MemoryStorageOptions());
|
x.UseMemoryStorage(new MemoryStorageOptions());
|
||||||
//using RecurringJobAttribute to build RecurringJob automatically.
|
|
||||||
x.UseRecurringJob(typeof(ITestJob));
|
|
||||||
//x.UseActivator(new IoCJobActivator(services.BuildServiceProvider()));
|
//x.UseActivator(new IoCJobActivator(services.BuildServiceProvider()));
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
|
@ -79,6 +77,11 @@ namespace Ombi
|
||||||
app.UseHangfireServer();
|
app.UseHangfireServer();
|
||||||
app.UseHangfireDashboard();
|
app.UseHangfireDashboard();
|
||||||
|
|
||||||
|
|
||||||
|
// Setup the scheduler
|
||||||
|
var jobSetup = (IJobSetup)app.ApplicationServices.GetService(typeof(IJobSetup));
|
||||||
|
jobSetup.Setup();
|
||||||
|
|
||||||
ConfigureAuth(app);
|
ConfigureAuth(app);
|
||||||
|
|
||||||
var provider = new FileExtensionContentTypeProvider();
|
var provider = new FileExtensionContentTypeProvider();
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { SearchComponent } from './search/search.component';
|
||||||
import { MovieSearchComponent } from './search/moviesearch.component';
|
import { MovieSearchComponent } from './search/moviesearch.component';
|
||||||
import { RequestComponent } from './requests/request.component';
|
import { RequestComponent } from './requests/request.component';
|
||||||
import { LoginComponent } from './login/login.component';
|
import { LoginComponent } from './login/login.component';
|
||||||
|
import { LandingPageComponent } from './landingpage/landingpage.component';
|
||||||
import { PageNotFoundComponent } from './errors/not-found.component';
|
import { PageNotFoundComponent } from './errors/not-found.component';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
|
@ -42,6 +43,7 @@ const routes: Routes = [
|
||||||
{ path: 'search', component: SearchComponent, canActivate: [AuthGuard] },
|
{ path: 'search', component: SearchComponent, canActivate: [AuthGuard] },
|
||||||
{ path: 'requests', component: RequestComponent, canActivate: [AuthGuard] },
|
{ path: 'requests', component: RequestComponent, canActivate: [AuthGuard] },
|
||||||
{ path: 'login', component: LoginComponent },
|
{ path: 'login', component: LoginComponent },
|
||||||
|
{ path: 'landingpage', component: LandingPageComponent },
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -66,7 +68,8 @@ const routes: Routes = [
|
||||||
SearchComponent,
|
SearchComponent,
|
||||||
RequestComponent,
|
RequestComponent,
|
||||||
LoginComponent,
|
LoginComponent,
|
||||||
MovieSearchComponent
|
MovieSearchComponent,
|
||||||
|
LandingPageComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
SearchService,
|
SearchService,
|
||||||
|
|
|
@ -42,6 +42,7 @@ export interface ILandingPageSettings extends ISettings {
|
||||||
enabled: boolean,
|
enabled: boolean,
|
||||||
beforeLogin: boolean,
|
beforeLogin: boolean,
|
||||||
afterLogin: boolean,
|
afterLogin: boolean,
|
||||||
|
noticeEnabled: boolean,
|
||||||
noticeText: string,
|
noticeText: string,
|
||||||
noticeBackgroundColor: string,
|
noticeBackgroundColor: string,
|
||||||
timeLimit: boolean,
|
timeLimit: boolean,
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
.landing-box {
|
||||||
|
height: 150px;
|
||||||
|
background: #333333 !important;
|
||||||
|
border-radius: 2%
|
||||||
|
}
|
26
Ombi/Ombi/wwwroot/app/landingpage/landingpage.component.html
Normal file
26
Ombi/Ombi/wwwroot/app/landingpage/landingpage.component.html
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<div *ngIf="landingPageSettings">
|
||||||
|
|
||||||
|
<h1>Hey! Welcome back to {{websiteName}}</h1>
|
||||||
|
|
||||||
|
<h3 *ngIf="settings.noticeEnabled" style="background-color: {{settings.noticeBackgroundColor}}">{{settings.noticeText}}</h3>
|
||||||
|
<div class="col-md-3 landing-box">
|
||||||
|
<div>
|
||||||
|
Request Something
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-3 col-md-offset-1 landing-box">
|
||||||
|
<div>
|
||||||
|
View all your requests
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-3 col-md-offset-1 landing-box">
|
||||||
|
<div>
|
||||||
|
Report an issue
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
27
Ombi/Ombi/wwwroot/app/landingpage/landingpage.component.ts
Normal file
27
Ombi/Ombi/wwwroot/app/landingpage/landingpage.component.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { SettingsService } from '../services/settings.service';
|
||||||
|
import { ILandingPageSettings } from '../interfaces/ISettings';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ombi',
|
||||||
|
moduleId: module.id,
|
||||||
|
templateUrl: './landingpage.component.html',
|
||||||
|
styleUrls: ['./landingpage.component.css']
|
||||||
|
})
|
||||||
|
export class LandingPageComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor(private settingsService: SettingsService) { }
|
||||||
|
|
||||||
|
websiteName: string;
|
||||||
|
landingPageSettings: ILandingPageSettings;
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.settingsService.getLandingPage().subscribe(x => {
|
||||||
|
this.landingPageSettings = x;
|
||||||
|
console.log(x);
|
||||||
|
});
|
||||||
|
this.websiteName = "Ombi";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { AuthHttp } from 'angular2-jwt';
|
import { AuthHttp } from 'angular2-jwt';
|
||||||
|
import { Http } from '@angular/http';
|
||||||
import { Observable } from 'rxjs/Rx';
|
import { Observable } from 'rxjs/Rx';
|
||||||
|
|
||||||
import { ServiceAuthHelpers } from './service.helpers';
|
import { ServiceAuthHelpers } from './service.helpers';
|
||||||
|
@ -7,47 +8,49 @@ import { IOmbiSettings, IEmbySettings, IPlexSettings, ISonarrSettings,ILandingPa
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SettingsService extends ServiceAuthHelpers {
|
export class SettingsService extends ServiceAuthHelpers {
|
||||||
constructor(http: AuthHttp) {
|
constructor(public httpAuth: AuthHttp, private nonAuthHttp: Http) {
|
||||||
super(http, '/api/v1/Settings/');
|
super(httpAuth, '/api/v1/Settings/');
|
||||||
}
|
}
|
||||||
|
|
||||||
getOmbi(): Observable<IOmbiSettings> {
|
getOmbi(): Observable<IOmbiSettings> {
|
||||||
return this.http.get(`${this.url}/Ombi/`).map(this.extractData);
|
return this.httpAuth.get(`${this.url}/Ombi/`).map(this.extractData);
|
||||||
}
|
}
|
||||||
|
|
||||||
saveOmbi(settings: IOmbiSettings): Observable<boolean> {
|
saveOmbi(settings: IOmbiSettings): Observable<boolean> {
|
||||||
return this.http.post(`${this.url}/Ombi/`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData);
|
return this.httpAuth.post(`${this.url}/Ombi/`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData);
|
||||||
}
|
}
|
||||||
|
|
||||||
getEmby(): Observable<IEmbySettings> {
|
getEmby(): Observable<IEmbySettings> {
|
||||||
return this.http.get(`${this.url}/Emby/`).map(this.extractData);
|
return this.httpAuth.get(`${this.url}/Emby/`).map(this.extractData);
|
||||||
}
|
}
|
||||||
|
|
||||||
saveEmby(settings: IEmbySettings): Observable<boolean> {
|
saveEmby(settings: IEmbySettings): Observable<boolean> {
|
||||||
return this.http.post(`${this.url}/Emby/`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData);
|
return this.httpAuth.post(`${this.url}/Emby/`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPlex(): Observable<IPlexSettings> {
|
getPlex(): Observable<IPlexSettings> {
|
||||||
return this.http.get(`${this.url}/Plex/`).map(this.extractData);
|
return this.httpAuth.get(`${this.url}/Plex/`).map(this.extractData);
|
||||||
}
|
}
|
||||||
|
|
||||||
savePlex(settings: IPlexSettings): Observable<boolean> {
|
savePlex(settings: IPlexSettings): Observable<boolean> {
|
||||||
return this.http.post(`${this.url}/Plex/`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData);
|
return this.httpAuth.post(`${this.url}/Plex/`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSonarr(): Observable<ISonarrSettings> {
|
getSonarr(): Observable<ISonarrSettings> {
|
||||||
return this.http.get(`${this.url}/Sonarr`).map(this.extractData);
|
return this.httpAuth.get(`${this.url}/Sonarr`).map(this.extractData);
|
||||||
}
|
}
|
||||||
|
|
||||||
saveSonarr(settings: ISonarrSettings): Observable<boolean> {
|
saveSonarr(settings: ISonarrSettings): Observable<boolean> {
|
||||||
return this.http.post(`${this.url}/Sonarr`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData);
|
return this.httpAuth.post(`${this.url}/Sonarr`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Using http since we need it not to be authenticated to get the landing page settings
|
||||||
getLandingPage(): Observable<ILandingPageSettings> {
|
getLandingPage(): Observable<ILandingPageSettings> {
|
||||||
return this.http.get(`${this.url}/LandingPage`).map(this.extractData);
|
return this.nonAuthHttp.get(`${this.url}/LandingPage`).map(this.extractData);
|
||||||
}
|
}
|
||||||
|
|
||||||
saveLandingPage(settings: ILandingPageSettings): Observable<boolean> {
|
saveLandingPage(settings: ILandingPageSettings): Observable<boolean> {
|
||||||
return this.http.post(`${this.url}/LandingPage`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData);
|
return this.httpAuth.post(`${this.url}/LandingPage`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
<div class="col-sm-8 col-sm-push-1">
|
<div class="col-sm-8 col-sm-push-1" *ngIf="settings">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Landing Page Configuration</legend>
|
<legend>Landing Page Configuration</legend>
|
||||||
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<input type="checkbox" id="enable" name="enable" [(ngModel)]="settings.enable" ng-checked="settings.enable">
|
<input type="checkbox" id="enable" name="enable" [(ngModel)]="settings.enabled" ng-checked="settings.enabled">
|
||||||
<label for="enable">Enable</label>
|
<label for="enable">Enable</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -14,12 +14,12 @@ before_build:
|
||||||
- appveyor-retry dotnet restore
|
- appveyor-retry dotnet restore
|
||||||
- appveyor-retry npm install bower -g
|
- appveyor-retry npm install bower -g
|
||||||
- appveyor-retry npm install -g gulp
|
- appveyor-retry npm install -g gulp
|
||||||
#- appveyor-retry npm install -g typescript
|
|
||||||
- appveyor-retry npm install
|
- appveyor-retry npm install
|
||||||
- appveyor-retry bower install
|
- appveyor-retry bower install
|
||||||
- gulp publish
|
- gulp publish
|
||||||
build_script:
|
build_script:
|
||||||
- dotnet build
|
- dotnet build
|
||||||
|
- dotnet xunit
|
||||||
after_build:
|
after_build:
|
||||||
- dotnet publish -c Release -r win10-x64
|
- dotnet publish -c Release -r win10-x64
|
||||||
- dotnet publish -c Release -r osx.10.12-x64
|
- dotnet publish -c Release -r osx.10.12-x64
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue