Delete usless plugin/startup handling

This commit is contained in:
Zoe Roux 2024-04-01 04:25:58 +02:00
parent 13ad35a9ac
commit 9bd1e50de3
No known key found for this signature in database
34 changed files with 478 additions and 1484 deletions

View File

@ -8,7 +8,6 @@ COPY src/Directory.Build.props src/Directory.Build.props
COPY src/Kyoo.Authentication/Kyoo.Authentication.csproj src/Kyoo.Authentication/Kyoo.Authentication.csproj
COPY src/Kyoo.Abstractions/Kyoo.Abstractions.csproj src/Kyoo.Abstractions/Kyoo.Abstractions.csproj
COPY src/Kyoo.Core/Kyoo.Core.csproj src/Kyoo.Core/Kyoo.Core.csproj
COPY src/Kyoo.Host/Kyoo.Host.csproj src/Kyoo.Host/Kyoo.Host.csproj
COPY src/Kyoo.Postgresql/Kyoo.Postgresql.csproj src/Kyoo.Postgresql/Kyoo.Postgresql.csproj
COPY src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj
COPY src/Kyoo.RabbitMq/Kyoo.RabbitMq.csproj src/Kyoo.RabbitMq/Kyoo.RabbitMq.csproj

View File

@ -8,7 +8,6 @@ COPY src/Directory.Build.props src/Directory.Build.props
COPY src/Kyoo.Authentication/Kyoo.Authentication.csproj src/Kyoo.Authentication/Kyoo.Authentication.csproj
COPY src/Kyoo.Abstractions/Kyoo.Abstractions.csproj src/Kyoo.Abstractions/Kyoo.Abstractions.csproj
COPY src/Kyoo.Core/Kyoo.Core.csproj src/Kyoo.Core/Kyoo.Core.csproj
COPY src/Kyoo.Host/Kyoo.Host.csproj src/Kyoo.Host/Kyoo.Host.csproj
COPY src/Kyoo.Postgresql/Kyoo.Postgresql.csproj src/Kyoo.Postgresql/Kyoo.Postgresql.csproj
COPY src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj
COPY src/Kyoo.RabbitMq/Kyoo.RabbitMq.csproj src/Kyoo.RabbitMq/Kyoo.RabbitMq.csproj

View File

@ -11,7 +11,6 @@ COPY src/Directory.Build.props src/Directory.Build.props
COPY src/Kyoo.Authentication/Kyoo.Authentication.csproj src/Kyoo.Authentication/Kyoo.Authentication.csproj
COPY src/Kyoo.Abstractions/Kyoo.Abstractions.csproj src/Kyoo.Abstractions/Kyoo.Abstractions.csproj
COPY src/Kyoo.Core/Kyoo.Core.csproj src/Kyoo.Core/Kyoo.Core.csproj
COPY src/Kyoo.Host/Kyoo.Host.csproj src/Kyoo.Host/Kyoo.Host.csproj
COPY src/Kyoo.Postgresql/Kyoo.Postgresql.csproj src/Kyoo.Postgresql/Kyoo.Postgresql.csproj
COPY src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj
COPY src/Kyoo.RabbitMq/Kyoo.RabbitMq.csproj src/Kyoo.RabbitMq/Kyoo.RabbitMq.csproj

View File

@ -10,8 +10,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Authentication", "src\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Swagger", "src\Kyoo.Swagger\Kyoo.Swagger.csproj", "{7D1A7596-73F6-4D35-842E-A5AD9C620596}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Host", "src\Kyoo.Host\Kyoo.Host.csproj", "{0938459E-2E2B-457F-8120-7D8CA93866A6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Meilisearch", "src\Kyoo.Meilisearch\Kyoo.Meilisearch.csproj", "{F8E6018A-FD51-40EB-99FF-A26BA59F2762}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.RabbitMq", "src\Kyoo.RabbitMq\Kyoo.RabbitMq.csproj", "{B97AD4A8-E6E6-41CD-87DF-5F1326FD7198}"
@ -54,10 +52,6 @@ Global
{7D1A7596-73F6-4D35-842E-A5AD9C620596}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7D1A7596-73F6-4D35-842E-A5AD9C620596}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7D1A7596-73F6-4D35-842E-A5AD9C620596}.Release|Any CPU.Build.0 = Release|Any CPU
{0938459E-2E2B-457F-8120-7D8CA93866A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0938459E-2E2B-457F-8120-7D8CA93866A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0938459E-2E2B-457F-8120-7D8CA93866A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0938459E-2E2B-457F-8120-7D8CA93866A6}.Release|Any CPU.Build.0 = Release|Any CPU
{F8E6018A-FD51-40EB-99FF-A26BA59F2762}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F8E6018A-FD51-40EB-99FF-A26BA59F2762}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F8E6018A-FD51-40EB-99FF-A26BA59F2762}.Release|Any CPU.ActiveCfg = Release|Any CPU

View File

@ -1,4 +1,2 @@
--project
src/Kyoo.Postgresql/Kyoo.Postgresql.csproj
--msbuildprojectextensionspath
out/obj/Kyoo.Postgresql

View File

@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>default</LangVersion>
<Nullable>enable</Nullable>
<Company>Kyoo</Company>
<Authors>Kyoo</Authors>
<Copyright>Copyright (c) Kyoo</Copyright>

View File

@ -1,65 +0,0 @@
// Kyoo - A portable and vast media library solution.
// Copyright (c) Kyoo.
//
// See AUTHORS.md and LICENSE file in the project root for full license information.
//
// Kyoo is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// Kyoo is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using Autofac;
using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Abstractions.Controllers;
/// <summary>
/// A common interface used to discord plugins
/// </summary>
/// <remarks>
/// You can inject services in the IPlugin constructor.
/// You should only inject well known services like an ILogger, IConfiguration or IWebHostEnvironment.
/// </remarks>
public interface IPlugin
{
/// <summary>
/// The name of the plugin
/// </summary>
string Name { get; }
/// <summary>
/// An optional configuration step to allow a plugin to change asp net configurations.
/// </summary>
/// <seealso cref="SA"/>
IEnumerable<IStartupAction> ConfigureSteps => ArraySegment<IStartupAction>.Empty;
/// <summary>
/// A configure method that will be run on plugin's startup.
/// </summary>
/// <param name="builder">The autofac service container to register services.</param>
void Configure(ContainerBuilder builder)
{
// Skipped
}
/// <summary>
/// A configure method that will be run on plugin's startup.
/// This is available for libraries that build upon a <see cref="IServiceCollection"/>, for more precise
/// configuration use <see cref="Configure(Autofac.ContainerBuilder)"/>.
/// </summary>
/// <param name="services">A service container to register new services.</param>
void Configure(IServiceCollection services)
{
// Skipped
}
}

View File

@ -1,69 +0,0 @@
// Kyoo - A portable and vast media library solution.
// Copyright (c) Kyoo.
//
// See AUTHORS.md and LICENSE file in the project root for full license information.
//
// Kyoo is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// Kyoo is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using Kyoo.Abstractions.Models.Exceptions;
namespace Kyoo.Abstractions.Controllers;
/// <summary>
/// A manager to load plugins and retrieve information from them.
/// </summary>
public interface IPluginManager
{
/// <summary>
/// Get a single plugin that match the type and name given.
/// </summary>
/// <param name="name">The name of the plugin</param>
/// <typeparam name="T">The type of the plugin</typeparam>
/// <exception cref="ItemNotFoundException">If no plugins match the query</exception>
/// <returns>A plugin that match the queries</returns>
public T GetPlugin<T>(string name);
/// <summary>
/// Get all plugins of the given type.
/// </summary>
/// <typeparam name="T">The type of plugins to get</typeparam>
/// <returns>A list of plugins matching the given type or an empty list of none match.</returns>
public ICollection<T> GetPlugins<T>();
/// <summary>
/// Get all plugins currently running on Kyoo. This also includes deleted plugins if the app as not been restarted.
/// </summary>
/// <returns>All plugins currently loaded.</returns>
public ICollection<IPlugin> GetAllPlugins();
/// <summary>
/// Load plugins and their dependencies from the plugin directory.
/// </summary>
/// <param name="plugins">
/// An initial plugin list to use.
/// You should not try to put plugins from the plugins directory here as they will get automatically loaded.
/// </param>
public void LoadPlugins(ICollection<IPlugin> plugins);
/// <summary>
/// Load plugins and their dependencies from the plugin directory.
/// </summary>
/// <param name="plugins">
/// An initial plugin list to use.
/// You should not try to put plugins from the plugins directory here as they will get automatically loaded.
/// </param>
public void LoadPlugins(params Type[] plugins);
}

View File

@ -1,270 +0,0 @@
// Kyoo - A portable and vast media library solution.
// Copyright (c) Kyoo.
//
// See AUTHORS.md and LICENSE file in the project root for full license information.
//
// Kyoo is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// Kyoo is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
using System;
using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Abstractions.Controllers;
/// <summary>
/// A list of constant priorities used for <see cref="IStartupAction"/>'s <see cref="IStartupAction.Priority"/>.
/// It also contains helper methods for creating new <see cref="StartupAction"/>.
/// </summary>
public static class SA
{
/// <summary>
/// The highest predefined priority existing for <see cref="StartupAction"/>.
/// </summary>
public const int Before = 5000;
/// <summary>
/// Items defining routing (see IApplicationBuilder.UseRouting use this priority.
/// </summary>
public const int Routing = 4000;
/// <summary>
/// Actions defining new static files router use this priority.
/// </summary>
public const int StaticFiles = 3000;
/// <summary>
/// Actions calling IApplicationBuilder.UseAuthentication use this priority.
/// </summary>
public const int Authentication = 2000;
/// <summary>
/// Actions calling IApplicationBuilder.UseAuthorization use this priority.
/// </summary>
public const int Authorization = 1000;
/// <summary>
/// Action adding endpoint should use this priority (with a negative modificator if there is a catchall).
/// </summary>
public const int Endpoint = 0;
/// <summary>
/// The lowest predefined priority existing for <see cref="StartupAction"/>.
/// It should run after all other actions.
/// </summary>
public const int After = -1000;
/// <summary>
/// Create a new <see cref="StartupAction"/>.
/// </summary>
/// <param name="action">The action to run</param>
/// <param name="priority">The priority of the new action</param>
/// <returns>A new <see cref="StartupAction"/></returns>
public static StartupAction New(Action action, int priority) => new(action, priority);
/// <summary>
/// Create a new <see cref="StartupAction"/>.
/// </summary>
/// <param name="action">The action to run</param>
/// <param name="priority">The priority of the new action</param>
/// <typeparam name="T">A dependency that this action will use.</typeparam>
/// <returns>A new <see cref="StartupAction"/></returns>
public static StartupAction<T> New<T>(Action<T> action, int priority)
where T : notnull => new(action, priority);
/// <summary>
/// Create a new <see cref="StartupAction"/>.
/// </summary>
/// <param name="action">The action to run</param>
/// <param name="priority">The priority of the new action</param>
/// <typeparam name="T">A dependency that this action will use.</typeparam>
/// <typeparam name="T2">A second dependency that this action will use.</typeparam>
/// <returns>A new <see cref="StartupAction"/></returns>
public static StartupAction<T, T2> New<T, T2>(Action<T, T2> action, int priority)
where T : notnull
where T2 : notnull => new(action, priority);
/// <summary>
/// Create a new <see cref="StartupAction"/>.
/// </summary>
/// <param name="action">The action to run</param>
/// <param name="priority">The priority of the new action</param>
/// <typeparam name="T">A dependency that this action will use.</typeparam>
/// <typeparam name="T2">A second dependency that this action will use.</typeparam>
/// <typeparam name="T3">A third dependency that this action will use.</typeparam>
/// <returns>A new <see cref="StartupAction"/></returns>
public static StartupAction<T, T2, T3> New<T, T2, T3>(Action<T, T2, T3> action, int priority)
where T : notnull
where T2 : notnull
where T3 : notnull => new(action, priority);
/// <summary>
/// A <see cref="IStartupAction"/> with no dependencies.
/// </summary>
public class StartupAction : IStartupAction
{
/// <summary>
/// The action to execute at startup.
/// </summary>
private readonly Action _action;
/// <inheritdoc />
public int Priority { get; }
/// <summary>
/// Create a new <see cref="StartupAction"/>.
/// </summary>
/// <param name="action">The action to execute on startup.</param>
/// <param name="priority">The priority of this action (see <see cref="Priority"/>).</param>
public StartupAction(Action action, int priority)
{
_action = action;
Priority = priority;
}
/// <inheritdoc />
public void Run(IServiceProvider provider)
{
_action.Invoke();
}
}
/// <summary>
/// A <see cref="IStartupAction"/> with one dependencies.
/// </summary>
/// <typeparam name="T">The dependency to use.</typeparam>
public class StartupAction<T> : IStartupAction
where T : notnull
{
/// <summary>
/// The action to execute at startup.
/// </summary>
private readonly Action<T> _action;
/// <inheritdoc />
public int Priority { get; }
/// <summary>
/// Create a new <see cref="StartupAction{T}"/>.
/// </summary>
/// <param name="action">The action to execute on startup.</param>
/// <param name="priority">The priority of this action (see <see cref="Priority"/>).</param>
public StartupAction(Action<T> action, int priority)
{
_action = action;
Priority = priority;
}
/// <inheritdoc />
public void Run(IServiceProvider provider)
{
_action.Invoke(provider.GetRequiredService<T>());
}
}
/// <summary>
/// A <see cref="IStartupAction"/> with two dependencies.
/// </summary>
/// <typeparam name="T">The dependency to use.</typeparam>
/// <typeparam name="T2">The second dependency to use.</typeparam>
public class StartupAction<T, T2> : IStartupAction
where T : notnull
where T2 : notnull
{
/// <summary>
/// The action to execute at startup.
/// </summary>
private readonly Action<T, T2> _action;
/// <inheritdoc />
public int Priority { get; }
/// <summary>
/// Create a new <see cref="StartupAction{T, T2}"/>.
/// </summary>
/// <param name="action">The action to execute on startup.</param>
/// <param name="priority">The priority of this action (see <see cref="Priority"/>).</param>
public StartupAction(Action<T, T2> action, int priority)
{
_action = action;
Priority = priority;
}
/// <inheritdoc />
public void Run(IServiceProvider provider)
{
_action.Invoke(provider.GetRequiredService<T>(), provider.GetRequiredService<T2>());
}
}
/// <summary>
/// A <see cref="IStartupAction"/> with three dependencies.
/// </summary>
/// <typeparam name="T">The dependency to use.</typeparam>
/// <typeparam name="T2">The second dependency to use.</typeparam>
/// <typeparam name="T3">The third dependency to use.</typeparam>
public class StartupAction<T, T2, T3> : IStartupAction
where T : notnull
where T2 : notnull
where T3 : notnull
{
/// <summary>
/// The action to execute at startup.
/// </summary>
private readonly Action<T, T2, T3> _action;
/// <inheritdoc />
public int Priority { get; }
/// <summary>
/// Create a new <see cref="StartupAction{T, T2, T3}"/>.
/// </summary>
/// <param name="action">The action to execute on startup.</param>
/// <param name="priority">The priority of this action (see <see cref="Priority"/>).</param>
public StartupAction(Action<T, T2, T3> action, int priority)
{
_action = action;
Priority = priority;
}
/// <inheritdoc />
public void Run(IServiceProvider provider)
{
_action.Invoke(
provider.GetRequiredService<T>(),
provider.GetRequiredService<T2>(),
provider.GetRequiredService<T3>()
);
}
}
}
/// <summary>
/// An action executed on kyoo's startup to initialize the asp-net container.
/// </summary>
/// <remarks>
/// This is the base interface, see <see cref="SA.StartupAction"/> for a simpler use of this.
/// </remarks>
public interface IStartupAction
{
/// <summary>
/// The priority of this action. The actions will be executed on descending priority order.
/// If two actions have the same priority, their order is undefined.
/// </summary>
int Priority { get; }
/// <summary>
/// Run this action to configure the container, a service provider containing all services can be used.
/// </summary>
/// <param name="provider">The service provider containing all services can be used.</param>
void Run(IServiceProvider provider);
}

View File

@ -3,7 +3,6 @@
<Title>Kyoo.Abstractions</Title>
<Description>Base package to create plugins for Kyoo.</Description>
<RootNamespace>Kyoo.Abstractions</RootNamespace>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>

View File

@ -16,7 +16,6 @@
// You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -28,62 +27,41 @@ using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Microsoft.IdentityModel.Tokens;
using Serilog;
namespace Kyoo.Authentication;
/// <summary>
/// A module that enable OpenID authentication for Kyoo.
/// </summary>
/// <remarks>
/// Create a new authentication module instance and use the given configuration.
/// </remarks>
public class AuthenticationModule(
IConfiguration configuration,
ILogger<AuthenticationModule> logger
) : IPlugin
public static class AuthenticationModule
{
/// <inheritdoc />
public string Name => "Authentication";
/// <summary>
/// The configuration to use.
/// </summary>
private readonly IConfiguration _configuration = configuration;
/// <inheritdoc />
public void Configure(ContainerBuilder builder)
public static void ConfigureAuthentication(this WebApplicationBuilder builder)
{
builder.RegisterType<PermissionValidator>().As<IPermissionValidator>().SingleInstance();
builder.RegisterType<TokenController>().As<ITokenController>().SingleInstance();
}
/// <inheritdoc />
public void Configure(IServiceCollection services)
{
string secret = _configuration.GetValue(
string secret = builder.Configuration.GetValue(
"AUTHENTICATION_SECRET",
AuthenticationOption.DefaultSecret
)!;
PermissionOption options =
new()
{
Default = _configuration
.GetValue("UNLOGGED_PERMISSIONS", "")!
Default = builder
.Configuration.GetValue("UNLOGGED_PERMISSIONS", "")!
.Split(',')
.Where(x => x.Length > 0)
.ToArray(),
NewUser = _configuration
.GetValue("DEFAULT_PERMISSIONS", "overall.read,overall.play")!
NewUser = builder
.Configuration.GetValue("DEFAULT_PERMISSIONS", "overall.read,overall.play")!
.Split(','),
RequireVerification = _configuration.GetValue("REQUIRE_ACCOUNT_VERIFICATION", true),
RequireVerification = builder.Configuration.GetValue(
"REQUIRE_ACCOUNT_VERIFICATION",
true
),
PublicUrl =
_configuration.GetValue<string?>("PUBLIC_URL") ?? "http://localhost:8901",
ApiKeys = _configuration.GetValue("KYOO_APIKEYS", string.Empty)!.Split(','),
OIDC = _configuration
.AsEnumerable()
builder.Configuration.GetValue<string?>("PUBLIC_URL")
?? "http://localhost:8901",
ApiKeys = builder.Configuration.GetValue("KYOO_APIKEYS", string.Empty)!.Split(','),
OIDC = builder
.Configuration.AsEnumerable()
.Where((pair) => pair.Key.StartsWith("OIDC_"))
.Aggregate(
new Dictionary<string, OidcProvider>(),
@ -93,7 +71,7 @@ public class AuthenticationModule(
return acc;
if (val.Key.Split("_") is not ["OIDC", string provider, string key])
{
logger.LogError("Invalid oidc config value: {Key}", val.Key);
Log.Error("Invalid oidc config value: {Key}", val.Key);
return acc;
}
provider = provider.ToLowerInvariant();
@ -129,20 +107,20 @@ public class AuthenticationModule(
acc[provider].LogoUrl = val.Value;
break;
default:
logger.LogError("Invalid oidc config value: {Key}", key);
Log.Error("Invalid oidc config value: {Key}", key);
return acc;
}
return acc;
}
),
};
services.AddSingleton(options);
services.AddSingleton(
builder.Services.AddSingleton(options);
builder.Services.AddSingleton(
new AuthenticationOption() { Secret = secret, Permissions = options, }
);
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
builder
.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Events = new()
@ -171,12 +149,8 @@ public class AuthenticationModule(
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret))
};
});
}
/// <inheritdoc />
public IEnumerable<IStartupAction> ConfigureSteps =>
new IStartupAction[]
{
SA.New<IApplicationBuilder>(app => app.UseAuthentication(), SA.Authentication),
};
builder.Services.AddSingleton<IPermissionValidator, PermissionValidator>();
builder.Services.AddSingleton<ITokenController, TokenController>();
}
}

View File

@ -1,12 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.3" />
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Serilog" Version="3.1.1" />
<ProjectReference Include="../Kyoo.Abstractions/Kyoo.Abstractions.csproj" />
</ItemGroup>

View File

@ -53,8 +53,8 @@ public class LibraryManager : ILibraryManager
Studios = studioRepository;
Users = userRepository;
_repositories = new IBaseRepository[]
{
_repositories =
[
LibraryItems,
News,
Collections,
@ -64,7 +64,7 @@ public class LibraryManager : ILibraryManager
Episodes,
Studios,
Users
};
];
}
/// <inheritdoc />

View File

@ -42,7 +42,7 @@ public class ThumbnailsManager(
Lazy<IRepository<User>> users
) : IThumbnailsManager
{
private static readonly Dictionary<string, TaskCompletionSource<object>> _downloading = new();
private static readonly Dictionary<string, TaskCompletionSource<object>> _downloading = [];
private static async Task _WriteTo(SKBitmap bitmap, string path, int quality)
{

View File

@ -17,29 +17,15 @@
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using AspNetCore.Proxy;
using Autofac;
using Kyoo.Abstractions;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models.Utils;
using Kyoo.Core.Api;
using Kyoo.Abstractions.Models;
using Kyoo.Core.Controllers;
using Kyoo.Utils;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Core;
/// <summary>
/// The core module containing default implementations
/// </summary>
public class CoreModule : IPlugin
public static class CoreModule
{
/// <summary>
/// A service provider to access services in static context (in events for example).
@ -47,102 +33,31 @@ public class CoreModule : IPlugin
/// <remarks>Don't forget to create a scope.</remarks>
public static IServiceProvider Services { get; set; }
/// <inheritdoc />
public string Name => "Core";
/// <inheritdoc />
public void Configure(ContainerBuilder builder)
public static void AddRepository<T, TRepo>(this IServiceCollection services)
where T:IResource
where TRepo : class, IRepository<T>
{
builder
.RegisterType<ThumbnailsManager>()
.As<IThumbnailsManager>()
.InstancePerLifetimeScope();
builder.RegisterType<LibraryManager>().As<ILibraryManager>().InstancePerLifetimeScope();
builder.RegisterRepository<LibraryItemRepository>();
builder.RegisterRepository<CollectionRepository>();
builder.RegisterRepository<MovieRepository>();
builder.RegisterRepository<ShowRepository>();
builder.RegisterRepository<SeasonRepository>();
builder.RegisterRepository<EpisodeRepository>();
builder.RegisterRepository<StudioRepository>();
builder.RegisterRepository<UserRepository>().As<IUserRepository>();
builder.RegisterRepository<NewsRepository>();
builder
.RegisterType<WatchStatusRepository>()
.As<IWatchStatusRepository>()
.AsSelf()
.InstancePerLifetimeScope();
builder
.RegisterType<IssueRepository>()
.As<IIssueRepository>()
.AsSelf()
.InstancePerLifetimeScope();
builder.RegisterType<SqlVariableContext>().InstancePerLifetimeScope();
services.AddScoped<TRepo>();
services.AddScoped<IRepository<T>>(x => x.GetRequiredService<TRepo>());
}
/// <inheritdoc />
public void Configure(IServiceCollection services)
public static void ConfigureKyoo(this WebApplicationBuilder builder)
{
services.AddHttpContextAccessor();
builder.Services.AddScoped<IThumbnailsManager, ThumbnailsManager>();
builder.Services.AddScoped<ILibraryManager, LibraryManager>();
services
.AddMvcCore(options =>
{
options.Filters.Add<ExceptionFilter>();
options.ModelBinderProviders.Insert(0, new SortBinder.Provider());
options.ModelBinderProviders.Insert(0, new IncludeBinder.Provider());
options.ModelBinderProviders.Insert(0, new FilterBinder.Provider());
})
.AddJsonOptions(x =>
{
x.JsonSerializerOptions.TypeInfoResolver = new JsonKindResolver()
{
Modifiers = { IncludeBinder.HandleLoadableFields }
};
x.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
x.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
})
.AddDataAnnotations()
.AddControllersAsServices()
.AddApiExplorer()
.ConfigureApiBehaviorOptions(options =>
{
options.SuppressMapClientErrors = true;
options.InvalidModelStateResponseFactory = ctx =>
{
string[] errors = ctx
.ModelState.SelectMany(x => x.Value!.Errors)
.Select(x => x.ErrorMessage)
.ToArray();
return new BadRequestObjectResult(new RequestError(errors));
};
});
services.Configure<RouteOptions>(x =>
{
x.ConstraintMap.Add("id", typeof(IdentifierRouteConstraint));
});
services.AddResponseCompression(x =>
{
x.EnableForHttps = true;
});
services.AddProxies();
services.AddHttpClient();
builder.Services.AddRepository<ILibraryItem, LibraryItemRepository>();
builder.Services.AddRepository<Collection, CollectionRepository>();
builder.Services.AddRepository<Movie, MovieRepository>();
builder.Services.AddRepository<Show, ShowRepository>();
builder.Services.AddRepository<Season, SeasonRepository>();
builder.Services.AddRepository<Episode, EpisodeRepository>();
builder.Services.AddRepository<Studio, StudioRepository>();
builder.Services.AddRepository<INews, NewsRepository>();
builder.Services.AddRepository<User, UserRepository>();
builder.Services.AddScoped<IUserRepository>(x => x.GetRequiredService<UserRepository>());
builder.Services.AddScoped<IWatchStatusRepository, WatchStatusRepository>();
builder.Services.AddScoped<IIssueRepository, IssueRepository>();
builder.Services.AddScoped<SqlVariableContext>();
}
/// <inheritdoc />
public IEnumerable<IStartupAction> ConfigureSteps =>
new IStartupAction[]
{
SA.New<IApplicationBuilder>(app => app.UseHsts(), SA.Before),
SA.New<IApplicationBuilder>(app => app.UseResponseCompression(), SA.Routing + 1),
SA.New<IApplicationBuilder>(app => app.UseRouting(), SA.Routing),
SA.New<IApplicationBuilder>(
app => app.UseEndpoints(x => x.MapControllers()),
SA.Endpoint
)
};
}

View File

@ -0,0 +1,86 @@
// Kyoo - A portable and vast media library solution.
// Copyright (c) Kyoo.
//
// See AUTHORS.md and LICENSE file in the project root for full license information.
//
// Kyoo is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// Kyoo is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using AspNetCore.Proxy;
using Kyoo.Abstractions.Models.Utils;
using Kyoo.Core.Api;
using Kyoo.Core.Controllers;
using Kyoo.Utils;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Core.Extensions;
public static class ServiceExtensions
{
public static void ConfigureMvc(this IServiceCollection services)
{
services.AddHttpContextAccessor();
services
.AddMvcCore(options =>
{
options.Filters.Add<ExceptionFilter>();
options.ModelBinderProviders.Insert(0, new SortBinder.Provider());
options.ModelBinderProviders.Insert(0, new IncludeBinder.Provider());
options.ModelBinderProviders.Insert(0, new FilterBinder.Provider());
})
.AddJsonOptions(x =>
{
x.JsonSerializerOptions.TypeInfoResolver = new JsonKindResolver()
{
Modifiers = { IncludeBinder.HandleLoadableFields }
};
x.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
x.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
})
.AddDataAnnotations()
.AddControllersAsServices()
.AddApiExplorer()
.ConfigureApiBehaviorOptions(options =>
{
options.SuppressMapClientErrors = true;
options.InvalidModelStateResponseFactory = ctx =>
{
string[] errors = ctx
.ModelState.SelectMany(x => x.Value!.Errors)
.Select(x => x.ErrorMessage)
.ToArray();
return new BadRequestObjectResult(new RequestError(errors));
};
});
services.Configure<RouteOptions>(x =>
{
x.ConstraintMap.Add("id", typeof(IdentifierRouteConstraint));
});
services.AddResponseCompression(x =>
{
x.EnableForHttps = true;
});
services.AddProxies();
services.AddHttpClient();
}
}

View File

@ -2,7 +2,10 @@
<PropertyGroup>
<AssemblyName>Kyoo.Core</AssemblyName>
<RootNamespace>Kyoo.Core</RootNamespace>
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<!-- Limit the number of threads, the default is to not limit so scanning the library
create way too many of them and slows the whole server. -->
<ThreadPoolMaxThreads>50</ThreadPoolMaxThreads>
</PropertyGroup>
<ItemGroup>
@ -12,6 +15,11 @@
<PackageReference Include="InterpolatedSql.Dapper" Version="2.3.0" />
<PackageReference Include="FlexLabs.EntityFrameworkCore.Upsert" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="6.0.0" />
<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
<PackageReference Include="Serilog.Expressions" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.SyslogMessages" Version="3.0.1" />
<PackageReference Include="SkiaSharp" Version="2.88.7" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.7" />
</ItemGroup>
@ -19,6 +27,9 @@
<ItemGroup>
<ProjectReference Include="../Kyoo.Abstractions/Kyoo.Abstractions.csproj" />
<ProjectReference Include="../Kyoo.Postgresql/Kyoo.Postgresql.csproj" />
<ProjectReference Include="../Kyoo.Meilisearch/Kyoo.Meilisearch.csproj" />
<ProjectReference Include="../Kyoo.RabbitMq/Kyoo.RabbitMq.csproj" />
<ProjectReference Include="../Kyoo.Authentication/Kyoo.Authentication.csproj" />
<ProjectReference Include="../Kyoo.Swagger/Kyoo.Swagger.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,109 @@
// Kyoo - A portable and vast media library solution.
// Copyright (c) Kyoo.
//
// See AUTHORS.md and LICENSE file in the project root for full license information.
//
// Kyoo is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// Kyoo is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
using System;
using System.IO;
using Kyoo.Authentication;
using Kyoo.Core;
using Kyoo.Core.Extensions;
using Kyoo.Meiliseach;
using Kyoo.Postgresql;
using Kyoo.RabbitMq;
using Kyoo.Swagger;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Events;
using Serilog.Templates;
using Serilog.Templates.Themes;
#if DEBUG
const string EnvironmentName = "Development";
#else
const string EnvironmentName = "Production";
#endif
WebApplicationBuilder builder = WebApplication.CreateBuilder(
new WebApplicationOptions()
{
Args = args,
EnvironmentName = EnvironmentName,
ApplicationName = "Kyoo",
ContentRootPath = AppDomain.CurrentDomain.BaseDirectory,
}
);
builder.WebHost.UseKestrel(opt =>
{
opt.AddServerHeader = false;
});
const string template =
"[{@t:HH:mm:ss} {@l:u3} {Substring(SourceContext, LastIndexOf(SourceContext, '.') + 1), 25} "
+ "({@i:D10})] {@m}{#if not EndsWith(@m, '\n')}\n{#end}{@x}";
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Warning()
.MinimumLevel.Override("Kyoo", LogEventLevel.Verbose)
.MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Verbose)
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Fatal)
.WriteTo.Console(new ExpressionTemplate(template, theme: TemplateTheme.Code))
.Enrich.WithThreadId()
.Enrich.FromLogContext()
.CreateLogger();
AppDomain.CurrentDomain.ProcessExit += (_, _) => Log.CloseAndFlush();
AppDomain.CurrentDomain.UnhandledException += (_, ex) =>
Log.Fatal(ex.ExceptionObject as Exception, "Unhandled exception");
builder.Host.UseSerilog();
// Set current directory, used by thumbnails for example.
string path = Path.GetFullPath(builder.Configuration.GetValue("DATADIR", "/kyoo")!);
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
Environment.CurrentDirectory = path;
Log.Information("Data directory: {DataDirectory}", Environment.CurrentDirectory);
builder.Services.ConfigureMvc();
builder.Services.ConfigureOpenApi();
builder.ConfigureKyoo();
builder.ConfigureAuthentication();
builder.ConfigurePostgres();
builder.ConfigureMeilisearch();
WebApplication app = builder.Build();
CoreModule.Services = app.Services;
app.UseHsts();
app.UseKyooOpenApi();
app.UseResponseCompression();
app.UseRouting();
app.UseAuthentication();
app.MapControllers();
// Activate services that always run in the background
app.Services.GetRequiredService<MeiliSync>();
app.Services.GetRequiredService<RabbitProducer>();
await using (AsyncServiceScope scope = app.Services.CreateAsyncScope())
{
await MeilisearchModule.Initialize(scope.ServiceProvider);
}
app.Run(Environment.GetEnvironmentVariable("KYOO_BIND_URL") ?? "http://*:5000");

View File

@ -1,195 +0,0 @@
// Kyoo - A portable and vast media library solution.
// Copyright (c) Kyoo.
//
// See AUTHORS.md and LICENSE file in the project root for full license information.
//
// Kyoo is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// Kyoo is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
using System;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Kyoo.Core;
using Kyoo.Meiliseach;
using Kyoo.Postgresql;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Events;
using Serilog.Templates;
using Serilog.Templates.Themes;
using ILogger = Serilog.ILogger;
namespace Kyoo.Host;
/// <summary>
/// Hosts of kyoo (main functions) generally only create a new <see cref="Application"/>
/// and return <see cref="Start(string[])"/>.
/// </summary>
public class Application
{
/// <summary>
/// The environment in witch Kyoo will run (ether "Production" or "Development").
/// </summary>
private readonly string _environment;
/// <summary>
/// The logger used for startup and error messages.
/// </summary>
private ILogger _logger;
/// <summary>
/// Create a new <see cref="Application"/> that will use the specified environment.
/// </summary>
/// <param name="environment">The environment to run in.</param>
public Application(string environment)
{
_environment = environment;
}
/// <summary>
/// Start the application with the given console args.
/// This is generally called from the Main entrypoint of Kyoo.
/// </summary>
/// <param name="args">The console arguments to use for kyoo.</param>
/// <returns>A task representing the whole process</returns>
public Task Start(string[] args)
{
return Start(args, _ => { });
}
/// <summary>
/// Start the application with the given console args.
/// This is generally called from the Main entrypoint of Kyoo.
/// </summary>
/// <param name="args">The console arguments to use for kyoo.</param>
/// <param name="configure">A custom action to configure the container before the start</param>
/// <returns>A task representing the whole process</returns>
public async Task Start(string[] args, Action<ContainerBuilder> configure)
{
IConfiguration parsed = _SetupConfig(new ConfigurationBuilder(), args).Build();
string path = Path.GetFullPath(parsed.GetValue("DATADIR", "/kyoo"));
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
Environment.CurrentDirectory = path;
LoggerConfiguration config = new();
_ConfigureLogging(config);
Log.Logger = config.CreateBootstrapLogger();
_logger = Log.Logger.ForContext<Application>();
AppDomain.CurrentDomain.ProcessExit += (_, _) => Log.CloseAndFlush();
AppDomain.CurrentDomain.UnhandledException += (_, ex) =>
Log.Fatal(ex.ExceptionObject as Exception, "Unhandled exception");
IHost host = _CreateWebHostBuilder(args).ConfigureContainer(configure).Build();
await using (AsyncServiceScope scope = host.Services.CreateAsyncScope())
{
await MeilisearchModule.Initialize(scope.ServiceProvider);
}
await _StartWithHost(host);
}
/// <summary>
/// Start the given host and log failing exceptions.
/// </summary>
/// <param name="host">The host to start.</param>
/// <param name="cancellationToken">A token to allow one to stop the host.</param>
private async Task _StartWithHost(IHost host, CancellationToken cancellationToken = default)
{
try
{
CoreModule.Services = host.Services;
_logger.Information(
"Version: {Version}",
Assembly.GetExecutingAssembly().GetName().Version.ToString(3)
);
_logger.Information("Data directory: {DataDirectory}", Environment.CurrentDirectory);
await host.RunAsync(cancellationToken);
}
catch (Exception ex)
{
_logger.Fatal(ex, "Unhandled exception");
}
}
/// <summary>
/// Create a a web host
/// </summary>
/// <param name="args">Command line parameters that can be handled by kestrel</param>
/// <returns>A new web host instance</returns>
private IHostBuilder _CreateWebHostBuilder(string[] args)
{
return new HostBuilder()
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.UseContentRoot(AppDomain.CurrentDomain.BaseDirectory)
.UseEnvironment(_environment)
.ConfigureAppConfiguration(x => _SetupConfig(x, args))
.UseSerilog((host, services, builder) => _ConfigureLogging(builder))
.ConfigureServices(x => x.AddRouting())
.ConfigureWebHost(x =>
x.UseKestrel(options =>
{
options.AddServerHeader = false;
})
.UseIIS()
.UseIISIntegration()
.UseUrls(Environment.GetEnvironmentVariable("KYOO_BIND_URL") ?? "http://*:5000")
.UseStartup(host =>
PluginsStartup.FromWebHost(host, new LoggerFactory().AddSerilog())
)
);
}
/// <summary>
/// Register settings.json, environment variables and command lines arguments as configuration.
/// </summary>
/// <param name="builder">The configuration builder to use</param>
/// <param name="args">The command line arguments</param>
/// <returns>The modified configuration builder</returns>
private IConfigurationBuilder _SetupConfig(IConfigurationBuilder builder, string[] args)
{
return builder
.AddEnvironmentVariables()
.AddEnvironmentVariables("KYOO_")
.AddCommandLine(args);
}
/// <summary>
/// Configure the logging.
/// </summary>
/// <param name="builder">The logger builder to configure.</param>
private void _ConfigureLogging(LoggerConfiguration builder)
{
const string template =
"[{@t:HH:mm:ss} {@l:u3} {Substring(SourceContext, LastIndexOf(SourceContext, '.') + 1), 25} "
+ "({@i:D10})] {@m}{#if not EndsWith(@m, '\n')}\n{#end}{@x}";
builder
.MinimumLevel.Warning()
.MinimumLevel.Override("Kyoo", LogEventLevel.Verbose)
.MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Verbose)
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Fatal)
.WriteTo.Console(new ExpressionTemplate(template, theme: TemplateTheme.Code))
.Enrich.WithThreadId()
.Enrich.FromLogContext();
}
}

View File

@ -1,92 +0,0 @@
// Kyoo - A portable and vast media library solution.
// Copyright (c) Kyoo.
//
// See AUTHORS.md and LICENSE file in the project root for full license information.
//
// Kyoo is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// Kyoo is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using System.Linq;
using Kyoo.Abstractions.Controllers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Kyoo.Host.Controllers;
/// <summary>
/// An implementation of <see cref="IPluginManager"/>.
/// This is used to load plugins and retrieve information from them.
/// </summary>
public class PluginManager : IPluginManager
{
/// <summary>
/// The service provider. It allow plugin's activation.
/// </summary>
private readonly IServiceProvider _provider;
/// <summary>
/// The logger used by this class.
/// </summary>
private readonly ILogger<PluginManager> _logger;
/// <summary>
/// The list of plugins that are currently loaded.
/// </summary>
private readonly List<IPlugin> _plugins = new();
/// <summary>
/// Create a new <see cref="PluginManager"/> instance.
/// </summary>
/// <param name="provider">A service container to allow initialization of plugins</param>
/// <param name="logger">The logger used by this class.</param>
public PluginManager(IServiceProvider provider, ILogger<PluginManager> logger)
{
_provider = provider;
_logger = logger;
}
/// <inheritdoc />
public T GetPlugin<T>(string name)
{
return (T)_plugins?.FirstOrDefault(x => x.Name == name && x is T);
}
/// <inheritdoc />
public ICollection<T> GetPlugins<T>()
{
return _plugins?.OfType<T>().ToArray();
}
/// <inheritdoc />
public ICollection<IPlugin> GetAllPlugins()
{
return _plugins;
}
/// <inheritdoc />
public void LoadPlugins(ICollection<IPlugin> plugins)
{
_plugins.AddRange(plugins);
_logger.LogInformation("Modules enabled: {Plugins}", _plugins.Select(x => x.Name));
}
/// <inheritdoc />
public void LoadPlugins(params Type[] plugins)
{
LoadPlugins(
plugins.Select(x => (IPlugin)ActivatorUtilities.CreateInstance(_provider, x)).ToArray()
);
}
}

View File

@ -1,60 +0,0 @@
// Kyoo - A portable and vast media library solution.
// Copyright (c) Kyoo.
//
// See AUTHORS.md and LICENSE file in the project root for full license information.
//
// Kyoo is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// Kyoo is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
using System.Collections.Generic;
using Autofac;
using Autofac.Extras.AttributeMetadata;
using Kyoo.Abstractions.Controllers;
using Microsoft.AspNetCore.Builder;
using Serilog;
namespace Kyoo.Host;
/// <summary>
/// A module that registers host controllers and other needed things.
/// </summary>
public class HostModule : IPlugin
{
/// <inheritdoc />
public string Name => "Host";
/// <summary>
/// The plugin manager that loaded all plugins.
/// </summary>
private readonly IPluginManager _plugins;
/// <summary>
/// Create a new <see cref="HostModule"/>.
/// </summary>
/// <param name="plugins">The plugin manager that loaded all plugins.</param>
public HostModule(IPluginManager plugins)
{
_plugins = plugins;
}
/// <inheritdoc />
public void Configure(ContainerBuilder builder)
{
builder.RegisterModule<AttributedMetadataModule>();
builder.RegisterInstance(_plugins).As<IPluginManager>().ExternallyOwned();
}
/// <inheritdoc />
public IEnumerable<IStartupAction> ConfigureSteps =>
new[] { SA.New<IApplicationBuilder>(app => app.UseSerilogRequestLogging(), SA.Before) };
}

View File

@ -1,33 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<OutputType>Exe</OutputType>
<AssemblyName>Kyoo.Host</AssemblyName>
<RootNamespace>Kyoo.Host</RootNamespace>
<StartupObject>Kyoo.Host.Program</StartupObject>
<!-- Limit the number of threads, the default is to not limit so scanning the library
create way too many of them and slows the whole server. -->
<ThreadPoolMaxThreads>50</ThreadPoolMaxThreads>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
<PackageReference Include="Serilog.Expressions" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.SyslogMessages" Version="3.0.1" />
<PackageReference Include="System.Collections.Immutable" Version="8.0.0" />
<PackageReference Include="Autofac" Version="8.0.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="9.0.0" />
<PackageReference Include="Autofac.Extras.AttributeMetadata" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../Kyoo.Abstractions/Kyoo.Abstractions.csproj" />
<ProjectReference Include="../Kyoo.Core/Kyoo.Core.csproj" />
<ProjectReference Include="../Kyoo.Postgresql/Kyoo.Postgresql.csproj" />
<ProjectReference Include="../Kyoo.Meilisearch/Kyoo.Meilisearch.csproj" />
<ProjectReference Include="../Kyoo.RabbitMq/Kyoo.RabbitMq.csproj" />
<ProjectReference Include="../Kyoo.Authentication/Kyoo.Authentication.csproj" />
<ProjectReference Include="../Kyoo.Swagger/Kyoo.Swagger.csproj" />
</ItemGroup>
</Project>

View File

@ -1,195 +0,0 @@
// Kyoo - A portable and vast media library solution.
// Copyright (c) Kyoo.
//
// See AUTHORS.md and LICENSE file in the project root for full license information.
//
// Kyoo is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// Kyoo is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Autofac;
using Kyoo.Abstractions.Controllers;
using Kyoo.Authentication;
using Kyoo.Core;
using Kyoo.Host.Controllers;
using Kyoo.Meiliseach;
using Kyoo.Postgresql;
using Kyoo.RabbitMq;
using Kyoo.Swagger;
using Kyoo.Utils;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace Kyoo.Host;
/// <summary>
/// The Startup class is used to configure the AspNet's webhost.
/// </summary>
public class PluginsStartup
{
/// <summary>
/// A plugin manager used to load plugins and allow them to configure services / asp net.
/// </summary>
private readonly IPluginManager _plugins;
/// <summary>
/// The plugin that adds controllers and tasks specific to this host.
/// </summary>
private readonly IPlugin _hostModule;
/// <summary>
/// Created from the DI container, those services are needed to load information and instantiate plugins.s
/// </summary>
/// <param name="plugins">The plugin manager to use to load new plugins and configure the host.</param>
public PluginsStartup(IPluginManager plugins)
{
_plugins = plugins;
_hostModule = new HostModule(_plugins);
_plugins.LoadPlugins(
typeof(CoreModule),
typeof(AuthenticationModule),
typeof(PostgresModule),
typeof(MeilisearchModule),
typeof(RabbitMqModule),
typeof(SwaggerModule)
);
}
/// <summary>
/// Create a new <see cref="PluginsStartup"/> from a webhost.
/// This is meant to be used from <see cref="WebHostBuilderExtensions.UseStartup"/>.
/// </summary>
/// <param name="host">The context of the web host.</param>
/// <param name="logger">
/// The logger factory used to log while the application is setting itself up.
/// </param>
/// <returns>A new <see cref="PluginsStartup"/>.</returns>
public static PluginsStartup FromWebHost(WebHostBuilderContext host, ILoggerFactory logger)
{
HostServiceProvider hostProvider = new(host.HostingEnvironment, host.Configuration, logger);
PluginManager plugins = new(hostProvider, logger.CreateLogger<PluginManager>());
return new PluginsStartup(plugins);
}
/// <summary>
/// Configure the services context via the <see cref="PluginManager"/>.
/// </summary>
/// <param name="services">The service collection to fill.</param>
public void ConfigureServices(IServiceCollection services)
{
foreach (Assembly assembly in _plugins.GetAllPlugins().Select(x => x.GetType().Assembly))
services.AddMvcCore().AddApplicationPart(assembly);
_hostModule.Configure(services);
foreach (IPlugin plugin in _plugins.GetAllPlugins())
plugin.Configure(services);
}
/// <summary>
/// Configure the autofac container via the <see cref="PluginManager"/>.
/// </summary>
/// <param name="builder">The builder to configure.</param>
public void ConfigureContainer(ContainerBuilder builder)
{
_hostModule.Configure(builder);
foreach (IPlugin plugin in _plugins.GetAllPlugins())
plugin.Configure(builder);
}
/// <summary>
/// Configure the asp net host.
/// </summary>
/// <param name="app">The asp net host to configure</param>
/// <param name="container">An autofac container used to create a new scope to configure asp-net.</param>
public void Configure(IApplicationBuilder app, ILifetimeScope container)
{
IEnumerable<IStartupAction> steps = _plugins
.GetAllPlugins()
.Append(_hostModule)
.SelectMany(x => x.ConfigureSteps)
.OrderByDescending(x => x.Priority);
using ILifetimeScope scope = container.BeginLifetimeScope(x =>
x.RegisterInstance(app).SingleInstance().ExternallyOwned()
);
IServiceProvider provider = scope.Resolve<IServiceProvider>();
foreach (IStartupAction step in steps)
step.Run(provider);
}
/// <summary>
/// A simple host service provider used to activate plugins instance.
/// The same services as a generic host are available and an <see cref="ILoggerFactory"/> has been added.
/// </summary>
private class HostServiceProvider(
IWebHostEnvironment hostEnvironment,
IConfiguration configuration,
ILoggerFactory loggerFactory
) : IServiceProvider
{
/// <inheritdoc />
public object GetService(Type serviceType)
{
if (
serviceType == typeof(IWebHostEnvironment)
|| serviceType == typeof(IHostEnvironment)
)
return hostEnvironment;
if (serviceType == typeof(IConfiguration))
return configuration;
if (serviceType == typeof(IServiceProviderIsService))
return new ProviderIsService();
if (
serviceType.IsGenericType
&& serviceType.GetGenericTypeDefinition() == typeof(ILogger<>)
)
{
return Utility.RunGenericMethod<object>(
typeof(LoggerFactoryExtensions),
nameof(LoggerFactoryExtensions.CreateLogger),
serviceType.GetGenericArguments().First(),
loggerFactory
);
}
throw new ArgumentException(
$"{serviceType.Name} is not available in configuration stpe"
);
}
public class ProviderIsService : IServiceProviderIsService
{
public bool IsService(Type serviceType)
{
Type[] supported =
[
typeof(IWebHostEnvironment),
typeof(IHostEnvironment),
typeof(IConfiguration),
typeof(IServiceProviderIsService),
];
if (supported.Contains(serviceType))
return true;
return serviceType.IsGenericType
&& serviceType.GetGenericTypeDefinition() == typeof(ILogger<>);
}
}
}
}

View File

@ -1,48 +0,0 @@
// Kyoo - A portable and vast media library solution.
// Copyright (c) Kyoo.
//
// See AUTHORS.md and LICENSE file in the project root for full license information.
//
// Kyoo is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// Kyoo is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
namespace Kyoo.Host;
/// <summary>
/// Program entrypoint.
/// </summary>
public static class Program
{
/// <summary>
/// The string representation of the environment used in <see cref="IWebHostEnvironment"/>.
/// </summary>
#if DEBUG
private const string Environment = "Development";
#else
private const string Environment = "Production";
#endif
/// <summary>
/// Main function of the program
/// </summary>
/// <param name="args">Command line arguments</param>
/// <returns>A <see cref="Task"/> representing the lifetime of the program.</returns>
public static Task Main(string[] args)
{
Application application = new(Environment);
return application.Start(args);
}
}

View File

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Kyoo.Meilisearch</RootNamespace>
</PropertyGroup>

View File

@ -16,21 +16,18 @@
// You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
using Autofac;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models;
using Meilisearch;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using static System.Text.Json.JsonNamingPolicy;
namespace Kyoo.Meiliseach;
public class MeilisearchModule(IConfiguration configuration) : IPlugin
public static class MeilisearchModule
{
/// <inheritdoc />
public string Name => "Meilisearch";
public static Dictionary<string, Settings> IndexSettings =>
new()
{
@ -145,17 +142,16 @@ public class MeilisearchModule(IConfiguration configuration) : IPlugin
}
/// <inheritdoc />
public void Configure(ContainerBuilder builder)
public static void ConfigureMeilisearch(this WebApplicationBuilder builder)
{
builder
.RegisterInstance(
.Services.AddSingleton(
new MeilisearchClient(
configuration.GetValue("MEILI_HOST", "http://meilisearch:7700"),
configuration.GetValue<string?>("MEILI_MASTER_KEY")
builder.Configuration.GetValue("MEILI_HOST", "http://meilisearch:7700"),
builder.Configuration.GetValue<string?>("MEILI_MASTER_KEY")
)
)
.SingleInstance();
builder.RegisterType<MeiliSync>().AsSelf().SingleInstance().AutoActivate();
builder.RegisterType<SearchManager>().As<ISearchManager>().InstancePerLifetimeScope();
);
builder.Services.AddSingleton<ISearchManager, SearchManager>();
builder.Services.AddSingleton<MeiliSync>();
}
}

View File

@ -2,7 +2,6 @@
<PropertyGroup>
<AssemblyName>Kyoo.Postgresql</AssemblyName>
<RootNamespace>Kyoo.Postgresql</RootNamespace>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>

View File

@ -3,179 +3,178 @@ using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Kyoo.Postgresql.Migrations
namespace Kyoo.Postgresql.Migrations;
/// <inheritdoc />
public partial class UseDateOnly : Migration
{
/// <inheritdoc />
public partial class UseDateOnly : Migration
protected override void Up(MigrationBuilder migrationBuilder)
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder
.AlterDatabase()
.Annotation(
"Npgsql:Enum:genre",
"action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western"
)
.Annotation("Npgsql:Enum:status", "unknown,finished,airing,planned")
.Annotation("Npgsql:Enum:watch_status", "completed,watching,droped,planned,deleted")
.OldAnnotation(
"Npgsql:Enum:genre",
"action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western"
)
.OldAnnotation("Npgsql:Enum:status", "unknown,finished,airing,planned")
.OldAnnotation("Npgsql:Enum:watch_status", "completed,watching,droped,planned");
migrationBuilder
.AlterDatabase()
.Annotation(
"Npgsql:Enum:genre",
"action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western"
)
.Annotation("Npgsql:Enum:status", "unknown,finished,airing,planned")
.Annotation("Npgsql:Enum:watch_status", "completed,watching,droped,planned,deleted")
.OldAnnotation(
"Npgsql:Enum:genre",
"action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western"
)
.OldAnnotation("Npgsql:Enum:status", "unknown,finished,airing,planned")
.OldAnnotation("Npgsql:Enum:watch_status", "completed,watching,droped,planned");
migrationBuilder.AlterColumn<DateOnly>(
name: "start_air",
table: "shows",
type: "date",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true
migrationBuilder.AlterColumn<DateOnly>(
name: "start_air",
table: "shows",
type: "date",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true
);
migrationBuilder.AlterColumn<DateOnly>(
name: "end_air",
table: "shows",
type: "date",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true
);
migrationBuilder.AlterColumn<DateOnly>(
name: "start_date",
table: "seasons",
type: "date",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true
);
migrationBuilder.AlterColumn<DateOnly>(
name: "end_date",
table: "seasons",
type: "date",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true
);
migrationBuilder.AlterColumn<DateOnly>(
name: "air_date",
table: "movies",
type: "date",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true
);
migrationBuilder.AlterColumn<DateOnly>(
name: "release_date",
table: "episodes",
type: "date",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true
);
migrationBuilder.CreateIndex(
name: "ix_users_username",
table: "users",
column: "username",
unique: true
);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(name: "ix_users_username", table: "users");
migrationBuilder
.AlterDatabase()
.Annotation(
"Npgsql:Enum:genre",
"action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western"
)
.Annotation("Npgsql:Enum:status", "unknown,finished,airing,planned")
.Annotation("Npgsql:Enum:watch_status", "completed,watching,droped,planned")
.OldAnnotation(
"Npgsql:Enum:genre",
"action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western"
)
.OldAnnotation("Npgsql:Enum:status", "unknown,finished,airing,planned")
.OldAnnotation(
"Npgsql:Enum:watch_status",
"completed,watching,droped,planned,deleted"
);
migrationBuilder.AlterColumn<DateOnly>(
name: "end_air",
table: "shows",
type: "date",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true
);
migrationBuilder.AlterColumn<DateTime>(
name: "start_air",
table: "shows",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateOnly),
oldType: "date",
oldNullable: true
);
migrationBuilder.AlterColumn<DateOnly>(
name: "start_date",
table: "seasons",
type: "date",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true
);
migrationBuilder.AlterColumn<DateTime>(
name: "end_air",
table: "shows",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateOnly),
oldType: "date",
oldNullable: true
);
migrationBuilder.AlterColumn<DateOnly>(
name: "end_date",
table: "seasons",
type: "date",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true
);
migrationBuilder.AlterColumn<DateTime>(
name: "start_date",
table: "seasons",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateOnly),
oldType: "date",
oldNullable: true
);
migrationBuilder.AlterColumn<DateOnly>(
name: "air_date",
table: "movies",
type: "date",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true
);
migrationBuilder.AlterColumn<DateTime>(
name: "end_date",
table: "seasons",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateOnly),
oldType: "date",
oldNullable: true
);
migrationBuilder.AlterColumn<DateOnly>(
name: "release_date",
table: "episodes",
type: "date",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true
);
migrationBuilder.AlterColumn<DateTime>(
name: "air_date",
table: "movies",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateOnly),
oldType: "date",
oldNullable: true
);
migrationBuilder.CreateIndex(
name: "ix_users_username",
table: "users",
column: "username",
unique: true
);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(name: "ix_users_username", table: "users");
migrationBuilder
.AlterDatabase()
.Annotation(
"Npgsql:Enum:genre",
"action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western"
)
.Annotation("Npgsql:Enum:status", "unknown,finished,airing,planned")
.Annotation("Npgsql:Enum:watch_status", "completed,watching,droped,planned")
.OldAnnotation(
"Npgsql:Enum:genre",
"action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western"
)
.OldAnnotation("Npgsql:Enum:status", "unknown,finished,airing,planned")
.OldAnnotation(
"Npgsql:Enum:watch_status",
"completed,watching,droped,planned,deleted"
);
migrationBuilder.AlterColumn<DateTime>(
name: "start_air",
table: "shows",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateOnly),
oldType: "date",
oldNullable: true
);
migrationBuilder.AlterColumn<DateTime>(
name: "end_air",
table: "shows",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateOnly),
oldType: "date",
oldNullable: true
);
migrationBuilder.AlterColumn<DateTime>(
name: "start_date",
table: "seasons",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateOnly),
oldType: "date",
oldNullable: true
);
migrationBuilder.AlterColumn<DateTime>(
name: "end_date",
table: "seasons",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateOnly),
oldType: "date",
oldNullable: true
);
migrationBuilder.AlterColumn<DateTime>(
name: "air_date",
table: "movies",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateOnly),
oldType: "date",
oldNullable: true
);
migrationBuilder.AlterColumn<DateTime>(
name: "release_date",
table: "episodes",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateOnly),
oldType: "date",
oldNullable: true
);
}
migrationBuilder.AlterColumn<DateTime>(
name: "release_date",
table: "episodes",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateOnly),
oldType: "date",
oldNullable: true
);
}
}

View File

@ -33,39 +33,19 @@ using Npgsql;
namespace Kyoo.Postgresql;
/// <summary>
/// A postgresql implementation of <see cref="DatabaseContext"/>.
/// </summary>
public class PostgresContext : DatabaseContext
{
/// <summary>
/// Is this instance in debug mode?
/// </summary>
private readonly bool _debugMode;
/// <summary>
/// Should the configure step be skipped? This is used when the database is created via DbContextOptions.
/// </summary>
private readonly bool _skipConfigure;
/// <summary>
/// Design time constructor (dotnet ef migrations add). Do not use
/// </summary>
public PostgresContext()
: base(null!) { }
public PostgresContext(DbContextOptions options, IHttpContextAccessor accessor)
: base(options, accessor)
{
_skipConfigure = true;
}
public PostgresContext(string connection, bool debugMode, IHttpContextAccessor accessor)
: base(accessor)
{
_debugMode = debugMode;
}
/// <summary>
/// Set connection information for this database context
/// </summary>
@ -75,8 +55,6 @@ public class PostgresContext : DatabaseContext
if (!_skipConfigure)
{
optionsBuilder.UseNpgsql();
if (_debugMode)
optionsBuilder.EnableDetailedErrors().EnableSensitiveDataLogging();
}
optionsBuilder.UseSnakeCaseNamingConvention();

View File

@ -17,8 +17,8 @@
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
using System.Data.Common;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
@ -28,49 +28,42 @@ using Npgsql;
namespace Kyoo.Postgresql;
/// <summary>
/// A module to add postgresql capacity to the app.
/// </summary>
public class PostgresModule(IConfiguration configuration, IWebHostEnvironment environment) : IPlugin
public static class PostgresModule
{
/// <inheritdoc />
public string Name => "Postgresql";
/// <inheritdoc />
public void Configure(IServiceCollection services)
public static void ConfigurePostgres(this WebApplicationBuilder builder)
{
DbConnectionStringBuilder builder =
DbConnectionStringBuilder conBuilder =
new()
{
["USER ID"] = configuration.GetValue("POSTGRES_USER", "KyooUser"),
["PASSWORD"] = configuration.GetValue("POSTGRES_PASSWORD", "KyooPassword"),
["SERVER"] = configuration.GetValue("POSTGRES_SERVER", "db"),
["PORT"] = configuration.GetValue("POSTGRES_PORT", "5432"),
["DATABASE"] = configuration.GetValue("POSTGRES_DB", "kyooDB"),
["USER ID"] = builder.Configuration.GetValue("POSTGRES_USER", "KyooUser"),
["PASSWORD"] = builder.Configuration.GetValue("POSTGRES_PASSWORD", "KyooPassword"),
["SERVER"] = builder.Configuration.GetValue("POSTGRES_SERVER", "db"),
["PORT"] = builder.Configuration.GetValue("POSTGRES_PORT", "5432"),
["DATABASE"] = builder.Configuration.GetValue("POSTGRES_DB", "kyooDB"),
["POOLING"] = "true",
["MAXPOOLSIZE"] = "95",
["TIMEOUT"] = "30"
};
NpgsqlDataSourceBuilder dsBuilder = new(builder.ConnectionString);
NpgsqlDataSourceBuilder dsBuilder = new(conBuilder.ConnectionString);
dsBuilder.MapEnum<Status>();
dsBuilder.MapEnum<Genre>();
dsBuilder.MapEnum<WatchStatus>();
NpgsqlDataSource dataSource = dsBuilder.Build();
services.AddDbContext<DatabaseContext, PostgresContext>(
builder.Services.AddDbContext<DatabaseContext, PostgresContext>(
x =>
{
x.UseNpgsql(dataSource).UseProjectables();
if (environment.IsDevelopment())
if (builder.Environment.IsDevelopment())
x.EnableDetailedErrors().EnableSensitiveDataLogging();
},
ServiceLifetime.Transient
);
services.AddTransient(
builder.Services.AddTransient(
(services) => services.GetRequiredService<DatabaseContext>().Database.GetDbConnection()
);
services.AddHealthChecks().AddDbContextCheck<DatabaseContext>();
builder.Services.AddHealthChecks().AddDbContextCheck<DatabaseContext>();
}
}

View File

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Kyoo.RabbitMq</RootNamespace>
</PropertyGroup>

View File

@ -16,39 +16,30 @@
// You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
using Autofac;
using Kyoo.Abstractions.Controllers;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using RabbitMQ.Client;
namespace Kyoo.RabbitMq;
public class RabbitMqModule(IConfiguration configuration) : IPlugin
public static class RabbitMqModule
{
/// <inheritdoc />
public string Name => "RabbitMq";
/// <inheritdoc />
public void Configure(ContainerBuilder builder)
public static void ConfigureRabbitMq(this WebApplicationBuilder builder)
{
builder
.Register(
(_) =>
builder.Services.AddSingleton(_ =>
{
ConnectionFactory factory =
new()
{
ConnectionFactory factory =
new()
{
UserName = configuration.GetValue("RABBITMQ_DEFAULT_USER", "guest"),
Password = configuration.GetValue("RABBITMQ_DEFAULT_PASS", "guest"),
HostName = configuration.GetValue("RABBITMQ_HOST", "rabbitmq"),
Port = 5672,
};
UserName = builder.Configuration.GetValue("RABBITMQ_DEFAULT_USER", "guest"),
Password = builder.Configuration.GetValue("RABBITMQ_DEFAULT_PASS", "guest"),
HostName = builder.Configuration.GetValue("RABBITMQ_HOST", "rabbitmq"),
Port = 5672,
};
return factory.CreateConnection();
}
)
.AsSelf()
.SingleInstance();
builder.RegisterType<RabbitProducer>().AsSelf().SingleInstance().AutoActivate();
return factory.CreateConnection();
});
builder.Services.AddSingleton<RabbitProducer>();
}
}

View File

@ -2,6 +2,7 @@
<PropertyGroup>
<AssemblyName>Kyoo.Swagger</AssemblyName>
<RootNamespace>Kyoo.Swagger</RootNamespace>
<Nullable>disable</Nullable>
</PropertyGroup>
<ItemGroup>

View File

@ -18,7 +18,6 @@
using System.Collections.Generic;
using System.Reflection;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models.Utils;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
@ -31,16 +30,9 @@ using static Kyoo.Abstractions.Models.Utils.Constants;
namespace Kyoo.Swagger;
/// <summary>
/// A module to enable a swagger interface and an OpenAPI endpoint to document Kyoo.
/// </summary>
public class SwaggerModule : IPlugin
public static class SwaggerModule
{
/// <inheritdoc />
public string Name => "Swagger";
/// <inheritdoc />
public void Configure(IServiceCollection services)
public static void ConfigureOpenApi(this IServiceCollection services)
{
services.AddTransient<IApplicationModelProvider, GenericResponseProvider>();
services.AddOpenApiDocument(document =>
@ -106,24 +98,17 @@ public class SwaggerModule : IPlugin
});
}
/// <inheritdoc />
public IEnumerable<IStartupAction> ConfigureSteps =>
new IStartupAction[]
public static void UseKyooOpenApi(this IApplicationBuilder app)
{
app.UseOpenApi();
app.UseReDoc(x =>
{
SA.New<IApplicationBuilder>(app => app.UseOpenApi(), SA.Before + 1),
SA.New<IApplicationBuilder>(
app =>
app.UseReDoc(x =>
{
x.Path = "/doc";
x.TransformToExternalPath = (internalUiRoute, _) =>
"/api" + internalUiRoute;
x.AdditionalSettings["theme"] = new
{
colors = new { primary = new { main = "#e13e13" } }
};
}),
SA.Before
)
};
x.Path = "/doc";
x.TransformToExternalPath = (internalUiRoute, _) => "/api" + internalUiRoute;
x.AdditionalSettings["theme"] = new
{
colors = new { primary = new { main = "#e13e13" } }
};
});
}
}