diff --git a/NSSLServer.Core/Extension/IPlugin.cs b/NSSLServer.Core/Extension/IPlugin.cs index 93d3b2b..a80f83d 100644 --- a/NSSLServer.Core/Extension/IPlugin.cs +++ b/NSSLServer.Core/Extension/IPlugin.cs @@ -1,23 +1,27 @@ using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -namespace NSSLServer.Core.Extension -{ +namespace NSSLServer.Core.Extension; +/// +/// Interface for plugins +/// +/// +/// Has to contain empty ctor, otherwise not loaded +/// +public interface IPlugin +{ /// - /// Interface for plugins + /// The name of the plugin /// - /// - /// Has to contain empty ctor, otherwise not loaded - /// - public interface IPlugin - { - public string Name { get; } + string Name { get; } - bool Initialize(NLog.LogFactory logFactory); + /// + /// Configures the plugin during application startup. + /// + public void Configure(WebApplicationBuilder builder) { } - public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment environment) { } - public virtual void ConfigureServices(IServiceCollection services) { } - } + /// + /// Configures the plugin after all services are registered. + /// + public void Configure(WebApplication app) { } } diff --git a/NSSLServer.Core/HelperMethods/PluginCreator.cs b/NSSLServer.Core/HelperMethods/PluginCreator.cs index 60d0ae4..abef302 100644 --- a/NSSLServer.Core/HelperMethods/PluginCreator.cs +++ b/NSSLServer.Core/HelperMethods/PluginCreator.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.Extensions.Logging; +using System; using System.Linq.Expressions; namespace NSSLServer.Core.HelperMethods @@ -10,5 +11,10 @@ public static T GetInstance(Type pluginType) var body = Expression.New(pluginType); return Expression.Lambda>(body).Compile().Invoke(); } + + public static T GetInstance(Type pluginType, ILogger logger) + { + return (T)Activator.CreateInstance(pluginType, [logger]); + } } } diff --git a/NSSLServer.Core/NSSLServer.Core.csproj b/NSSLServer.Core/NSSLServer.Core.csproj index 58e507b..3360637 100644 --- a/NSSLServer.Core/NSSLServer.Core.csproj +++ b/NSSLServer.Core/NSSLServer.Core.csproj @@ -14,7 +14,6 @@ - diff --git a/NSSLServer.Database/Plugin.cs b/NSSLServer.Database/Plugin.cs index 6e6418b..5518d95 100644 --- a/NSSLServer.Database/Plugin.cs +++ b/NSSLServer.Database/Plugin.cs @@ -1,20 +1,18 @@ using Deviax.QueryBuilder; - -using NLog; +using Microsoft.AspNetCore.Builder; using NSSLServer.Core.Extension; -namespace NSSLServer.Database -{ - public class Plugin : IPlugin - { - public string Name { get; } = "Database Core Plugin"; +namespace NSSLServer.Database; - public bool Initialize(LogFactory logFactory) - { - QueryExecutor.DefaultExecutor = new PostgresExecutor(); +public class Plugin : IPlugin +{ + /// + public string Name { get; } = "Database Core Plugin"; - return true; - } + /// + public void Configure(WebApplication app) + { + QueryExecutor.DefaultExecutor = new PostgresExecutor(); } } diff --git a/NSSLServer.Database/Updater/DbUpdater.cs b/NSSLServer.Database/Updater/DbUpdater.cs index 0722762..f1979e8 100644 --- a/NSSLServer.Database/Updater/DbUpdater.cs +++ b/NSSLServer.Database/Updater/DbUpdater.cs @@ -1,7 +1,5 @@ using Deviax.QueryBuilder; - -using NLog; - +using Microsoft.Extensions.Logging; using NSSLServer.Database.Models; using System; @@ -21,13 +19,13 @@ public abstract class DbUpdater : IDbUpdater public abstract int Priority { get; } public bool UpToDate => CurrentVersion == DesiredVersion; - private Logger logger; + private ILogger logger; private List<(Version version, string path)> updateScriptPathes; - public DbUpdater() + public DbUpdater(ILogger logger) { var type = GetType();// "MyCompany.MyProduct.MyFile.txt"; - logger = LogManager.GetCurrentClassLogger(); + this.logger = logger; updateScriptPathes = new List<(Version, string)>(); @@ -55,7 +53,7 @@ public virtual async Task LoadCurrentVersion() } catch (Exception ex) { - logger.Warn(ex, $"Error loading current db version for {Name}, setting {nameof(CurrentVersion)} to default"); + logger.LogWarning(ex, "Error loading current db version for {name}, setting {currentVersion} to default", Name, nameof(CurrentVersion)); } if (dbVersion is null) { @@ -127,7 +125,7 @@ public async Task RunUpdates() catch (Exception ex) { trans.Rollback(); - logger.Error(ex, $"Error in {Name} for {updateScript.version}"); + logger.LogError(ex, "Error in {name} for {version}", Name, updateScript.version); break; } } diff --git a/NSSLServer.Database/Updater/FrameworkDbUpdater.cs b/NSSLServer.Database/Updater/FrameworkDbUpdater.cs index 97c8fdf..293e755 100644 --- a/NSSLServer.Database/Updater/FrameworkDbUpdater.cs +++ b/NSSLServer.Database/Updater/FrameworkDbUpdater.cs @@ -1,6 +1,6 @@  using Deviax.QueryBuilder; - +using Microsoft.Extensions.Logging; using NSSLServer.Database.Models; using NSSLServer.Models; using NSSLServer.Models.Products; @@ -8,7 +8,7 @@ namespace NSSLServer.Database.Updater { - public class FrameworkDbUpdater : DbUpdater + public class FrameworkDbUpdater(ILogger logger) : DbUpdater(logger) { public override int Priority { get; } = 1; diff --git a/NSSLServer.Plugin.Example/ExamplePlugin.cs b/NSSLServer.Plugin.Example/ExamplePlugin.cs index 837b38a..fb67c7b 100644 --- a/NSSLServer.Plugin.Example/ExamplePlugin.cs +++ b/NSSLServer.Plugin.Example/ExamplePlugin.cs @@ -1,21 +1,29 @@ -using NLog; - +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using NSSLServer.Core.Extension; -namespace NSSLServer.Plugin.Example +namespace NSSLServer.Plugin.Example; + +public class ExamplePlugin : IPlugin { - public class ExamplePlugin : IPlugin - { - private Logger logger; + private ILogger logger; - public string Name { get; } + /// + public string Name { get; } = "Example Plugin"; - public bool Initialize(LogFactory factory) - { - logger = factory.GetCurrentClassLogger(); - return true; - } + /// + public void Configure(WebApplicationBuilder builder) + { + // Register services here if needed. + } + /// + public void Configure(WebApplication app) + { + // Get services if needed. + logger = app.Services.GetRequiredService>(); + logger.LogInformation("Example Plugin loaded successfully."); } } diff --git a/NSSLServer.Plugin.InitializationHelper/Plugin.cs b/NSSLServer.Plugin.InitializationHelper/Plugin.cs index 35a6333..1256a01 100644 --- a/NSSLServer.Plugin.InitializationHelper/Plugin.cs +++ b/NSSLServer.Plugin.InitializationHelper/Plugin.cs @@ -1,100 +1,104 @@ -using System; +using Microsoft.AspNetCore.Builder; +using NSSLServer.Core.Extension; +using System; using System.IO; -using NLog; -using NSSLServer.Core.Extension; +namespace NSSLServer.Plugin.InitializationHelper; -namespace NSSLServer.Plugin.InitializationHelper +public class Plugin : IPlugin { - public class Plugin : IPlugin + public string Name { get; } = "Initialization Helper Plugin"; + + private void GetEmailCert() { - public string Name { get; } + var filePath = Path.Combine(Directory.GetCurrentDirectory(), "external", "emailcert"); + if (File.Exists(filePath)) + return; + File.WriteAllLines(filePath, new[] { "test", "test" }); + } - public bool Initialize(LogFactory logFactory) - { - AskForPostgres(); + private void CreateServiceAccountForFirebase() + { + if (File.Exists(Path.Combine(Directory.GetCurrentDirectory(), "external", "service_account.json"))) + return; + Console.WriteLine("You can create a firebase account, for firebase messaging. This service is free by google and normaly required for the shoppinglist plugin."); + Console.WriteLine("To do so, you can visit the firebase url and follow the stepts there: https://firebase.google.com/products/cloud-messaging"); + Console.WriteLine("After you've created everything, you should be able to download a 'service_account.json', please copy this file into the external folder, where the secretkey and connectionstring are at."); + Console.WriteLine("Path should be: " + Path.Combine(Directory.GetCurrentDirectory(), "external")); + Console.WriteLine("Press any key to continue..."); + Console.ReadKey(); + } - while (!GetConnectionString()) { }; + private bool GetSecretKeyForJwt() + { + if (File.Exists(Path.Combine(Directory.GetCurrentDirectory(), "external", "secretkey"))) + return true; - GetSecretKeyForJwt(); + Console.Clear(); + Console.WriteLine("Please insert a random string of letters for the jwt key, required for login token generation."); + Console.WriteLine("Can be changed afterwards in the 'secretkey' file in the external folder. After changing it, the current login tokens will be invalidated."); - CreateServiceAccountForFirebase(); + var jwtSecret = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(jwtSecret)) + return false; + Directory.CreateDirectory("external"); + File.WriteAllText(Path.Combine("external", "secretkey"), jwtSecret); - GetEmailCert(); + return true; + } + private bool GetConnectionString() + { + if (File.Exists(Path.Combine(Directory.GetCurrentDirectory(), "external", "connectionstring"))) return true; - } - - private void GetEmailCert() - { - var filePath = Path.Combine(Directory.GetCurrentDirectory(), "external", "emailcert"); - if (File.Exists(filePath)) - return; - File.WriteAllLines(filePath, new[] { "test", "test" }); - } - - private void CreateServiceAccountForFirebase() - { - if (File.Exists(Path.Combine(Directory.GetCurrentDirectory(), "external", "service_account.json"))) - return; - Console.WriteLine("You can create a firebase account, for firebase messaging. This service is free by google and normaly required for the shoppinglist plugin."); - Console.WriteLine("To do so, you can visit the firebase url and follow the stepts there: https://firebase.google.com/products/cloud-messaging"); - Console.WriteLine("After you've created everything, you should be able to download a 'service_account.json', please copy this file into the external folder, where the secretkey and connectionstring are at."); - Console.WriteLine("Path should be: " + Path.Combine(Directory.GetCurrentDirectory(), "external")); - Console.WriteLine("Press any key to continue..."); - Console.ReadKey(); - } - - private bool GetSecretKeyForJwt() - { - if (File.Exists(Path.Combine(Directory.GetCurrentDirectory(), "external", "secretkey"))) - return true; - - Console.Clear(); - Console.WriteLine("Please insert a random string of letters for the jwt key, required for login token generation."); - Console.WriteLine("Can be changed afterwards in the 'secretkey' file in the external folder. After changing it, the current login tokens will be invalidated."); - - var jwtSecret = Console.ReadLine(); - if (string.IsNullOrWhiteSpace(jwtSecret)) - return false; - Directory.CreateDirectory("external"); - File.WriteAllText(Path.Combine("external", "secretkey"), jwtSecret); + Console.Clear(); + Console.WriteLine("Please insert you connection string for the database."); + Console.WriteLine("Example: User Id=postgres;Server=127.0.0.1;Port=5432;Password=password;Database=testInit;Pooling=true;Minimum Pool Size=10;Trust Server Certificate=True;"); + Console.WriteLine("If you want to change the connectionstring afterwards, you have to edit the 'connectionstring' file in the external folder"); + + var conString = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(conString)) + return false; + Directory.CreateDirectory("external"); + File.WriteAllText(Path.Combine("external", "connectionstring"), conString); + return true; + } + + private bool AskForPostgres() + { + if (File.Exists(Path.Combine(Directory.GetCurrentDirectory(), "external", "connectionstring"))) return true; - } - - private bool GetConnectionString() - { - if (File.Exists(Path.Combine(Directory.GetCurrentDirectory(), "external", "connectionstring"))) - return true; - Console.Clear(); - Console.WriteLine("Please insert you connection string for the database."); - Console.WriteLine("Example: User Id=postgres;Server=127.0.0.1;Port=5432;Password=password;Database=testInit;Pooling=true;Minimum Pool Size=10;Trust Server Certificate=True;"); - Console.WriteLine("If you want to change the connectionstring afterwards, you have to edit the 'connectionstring' file in the external folder"); - - var conString = Console.ReadLine(); - if (string.IsNullOrWhiteSpace(conString)) - return false; - Directory.CreateDirectory("external"); - File.WriteAllText(Path.Combine("external", "connectionstring"), conString); + + Console.WriteLine("Do you have PostgreSQL installed? ((y)es, no)"); + var res = Console.ReadLine(); + + if (string.IsNullOrWhiteSpace(res)) + return true; + + var key = res.ToLowerInvariant(); + + if (key.StartsWith('y')) return true; - } - - private bool AskForPostgres() - { - if (File.Exists(Path.Combine(Directory.GetCurrentDirectory(), "external", "connectionstring"))) - return true; - Console.WriteLine("Do you have Postresql installed? ((y)es, no)"); - var res = Console.ReadLine(); - - if (!string.IsNullOrWhiteSpace(res) || res.ToLower().StartsWith("n")) - { - Console.Clear(); - Console.WriteLine("Please install Postgres first. You can download it from https://www.postgresql.org/download/"); - Console.WriteLine("If you have installed it, press any key"); - Console.ReadKey(); - res = null; - } - return string.IsNullOrWhiteSpace(res) || res.ToLower().StartsWith("y"); - } + + Console.Clear(); + Console.WriteLine("Please install Postgres first. You can download it from https://www.postgresql.org/download/"); + Console.WriteLine("If you have installed it, press any key"); + Console.ReadKey(); + + return true; + } + + /// + public void Configure(WebApplicationBuilder builder) + { + AskForPostgres(); + + while (!GetConnectionString()) { } + + GetSecretKeyForJwt(); + + CreateServiceAccountForFirebase(); + + GetEmailCert(); } } diff --git a/NSSLServer.Plugin.OpenFoodFacts/Plugin.cs b/NSSLServer.Plugin.OpenFoodFacts/Plugin.cs index d83889e..a08f8b4 100644 --- a/NSSLServer.Plugin.OpenFoodFacts/Plugin.cs +++ b/NSSLServer.Plugin.OpenFoodFacts/Plugin.cs @@ -1,33 +1,20 @@ -using Microsoft.Extensions.DependencyInjection; - -using NLog; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; using NSSLServer.Core.Extension; using NSSLServer.Plugin.Products.Core; using NSSLServer.Plugin.Shoppinglist.Sources; -namespace NSSLServer.Plugin.OpenFoodFacts -{ - public class Plugin : IPlugin - { - private Logger logger; - - public string Name => "OpenFood Facts API Plugin"; - - internal ServiceProvider ServiceProvider { get; private set; } +namespace NSSLServer.Plugin.OpenFoodFacts; - public bool Initialize(LogFactory factory) - { - logger = factory.GetCurrentClassLogger(); - - - return true; - } +public class Plugin : IPlugin +{ + /// + public string Name { get; } = "OpenFood Facts API Plugin"; - void IPlugin.ConfigureServices(Microsoft.Extensions.DependencyInjection.IServiceCollection services) - { - ServiceProvider = services.BuildServiceProvider(); - ProductSources.Instance.AddNewSource(ActivatorUtilities.CreateInstance(ServiceProvider)); - } + /// + public void Configure(WebApplication app) + { + ProductSources.Instance.AddNewSource(ActivatorUtilities.CreateInstance(app.Services)); } } diff --git a/NSSLServer.Plugin.Products.Core/ProductsPlugin.cs b/NSSLServer.Plugin.Products.Core/ProductsPlugin.cs index 4e46c87..ded1775 100644 --- a/NSSLServer.Plugin.Products.Core/ProductsPlugin.cs +++ b/NSSLServer.Plugin.Products.Core/ProductsPlugin.cs @@ -1,21 +1,8 @@ -using NLog; +using NSSLServer.Core.Extension; -using NSSLServer.Core.Extension; +namespace NSSLServer.Plugin.Products.Core; -namespace NSSLServer.Plugin.Products.Core +public class ProductsPlugin : IPlugin { - public class ProductsPlugin : IPlugin - { - private Logger logger; - - public string Name { get; } - - public bool Initialize(LogFactory factory) - { - logger = factory.GetCurrentClassLogger(); - return true; - } - - - } + public string Name { get; } = "Products Core Plugin"; } diff --git a/NSSLServer.Plugin.Recipes/RecipePlugin.cs b/NSSLServer.Plugin.Recipes/RecipePlugin.cs index 9eaa0f8..f102dab 100644 --- a/NSSLServer.Plugin.Recipes/RecipePlugin.cs +++ b/NSSLServer.Plugin.Recipes/RecipePlugin.cs @@ -1,19 +1,10 @@ -using NLog; -using NSSLServer.Core.Extension; +using NSSLServer.Core.Extension; -namespace NSSLServer.Plugin.Recipes -{ - public class RecipePlugin : IPlugin - { - public string Name => nameof(RecipePlugin); - - internal static Logger Logger { get; private set; } +namespace NSSLServer.Plugin.Recipes; - public bool Initialize(LogFactory factory) - { - Logger = factory.GetCurrentClassLogger(); - - return true; - } - } +/// +public class RecipePlugin : IPlugin +{ + /// + public string Name { get; } = nameof(RecipePlugin); } \ No newline at end of file diff --git a/NSSLServer.Plugin.Shoppinglist/Controller/ShoppingListModule.cs b/NSSLServer.Plugin.Shoppinglist/Controller/ShoppingListModule.cs index 4b3574c..5eebfb5 100644 --- a/NSSLServer.Plugin.Shoppinglist/Controller/ShoppingListModule.cs +++ b/NSSLServer.Plugin.Shoppinglist/Controller/ShoppingListModule.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Mvc; - +using Microsoft.Extensions.Logging; using NSSLServer.Database; using NSSLServer.Database.Attributes; using NSSLServer.Models; @@ -143,7 +143,7 @@ public async Task BatchAction(int listId, string command, [FromBo [HttpPost, Route("{listId}/products")] public async Task AddProduct(int listId, [FromBody] AddProductArgs args) { - Plugin.Logger.Warn($"Adding product with name \"{args.ProductName}\" and gtin \"{args.Gtin}\" to list {listId}"); + Plugin.Logger.LogWarning("""Adding product with name "{productName}" and gtin "{gtin}" to list {listId}""", args.ProductName, args.Gtin, listId); if (listId == 0 || ((string.IsNullOrWhiteSpace(args.Gtin) || !ulong.TryParse(args.Gtin, out _)) diff --git a/NSSLServer.Plugin.Shoppinglist/Plugin.cs b/NSSLServer.Plugin.Shoppinglist/Plugin.cs index 4100da0..14e64cc 100644 --- a/NSSLServer.Plugin.Shoppinglist/Plugin.cs +++ b/NSSLServer.Plugin.Shoppinglist/Plugin.cs @@ -1,31 +1,36 @@ using FirebaseAdmin; - -using NLog; - +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using NSSLServer.Core.Extension; using NSSLServer.Plugin.Products.Core; using NSSLServer.Plugin.Shoppinglist.Sources; using System.IO; -namespace NSSLServer.Plugin.Shoppinglist +namespace NSSLServer.Plugin.Shoppinglist; + +public class Plugin : IPlugin { - public class Plugin : IPlugin - { - public string Name { get; } = "Plugin Shoppinglist"; - public static Logger Logger { get; private set; } + /// + public string Name { get; } = "Plugin Shoppinglist"; + + internal static ILogger Logger { get; private set; } - public bool Initialize(LogFactory logFactory) + /// + public void Configure(WebApplicationBuilder builder) + { + if (File.Exists("external/service_account.json")) { - Logger = logFactory.GetCurrentClassLogger(); - if (File.Exists("external/service_account.json")) - { - FirebaseApp.Create(new AppOptions { Credential = Google.Apis.Auth.OAuth2.GoogleCredential.FromFile("external/service_account.json") }); - } + FirebaseApp.Create(new AppOptions { Credential = Google.Apis.Auth.OAuth2.GoogleCredential.FromFile("external/service_account.json") }); + } - ProductSources.Instance.AddNewSource(new ProductSource()); - ProductSources.Instance.AddNewSource(new OutpanProductSource()); - return true; + ProductSources.Instance.AddNewSource(new ProductSource()); + ProductSources.Instance.AddNewSource(new OutpanProductSource()); + } - } + /// + public void Configure(WebApplication app) + { + Logger = app.Services.GetRequiredService>(); } } diff --git a/NSSLServer.Plugin.Userhanlding/Plugin.cs b/NSSLServer.Plugin.Userhanlding/Plugin.cs index 2765e22..1730eea 100644 --- a/NSSLServer.Plugin.Userhanlding/Plugin.cs +++ b/NSSLServer.Plugin.Userhanlding/Plugin.cs @@ -1,18 +1,18 @@ -using NLog; +using Microsoft.AspNetCore.Builder; using NSSLServer.Core.Extension; using NSSLServer.Plugin.Userhandling.Manager; -namespace NSSLServer.Plugin.Userhandling +namespace NSSLServer.Plugin.Userhandling; + +public class Plugin : IPlugin { - public class Plugin : IPlugin - { - public string Name { get; } = "Plugin Userhandling"; + /// + public string Name { get; } = "Plugin Userhandling"; - public bool Initialize(LogFactory logFactory) - { - UserManager.ReadLoginInformation(); - return true; - } + /// + public void Configure(WebApplicationBuilder builder) + { + UserManager.ReadLoginInformation(); } } diff --git a/NSSLServer.Plugin.Webapp/Plugin.cs b/NSSLServer.Plugin.Webapp/Plugin.cs index 5293ef5..3d0c7c5 100644 --- a/NSSLServer.Plugin.Webapp/Plugin.cs +++ b/NSSLServer.Plugin.Webapp/Plugin.cs @@ -1,42 +1,28 @@ using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.FileProviders; -using NLog; - using NSSLServer.Core.Extension; using System.IO; -namespace NSSLServer.Plugin.Webapp -{ - public class Plugin : IPlugin - { - public string Name => "WebappPlugin"; - - internal static Logger Logger { get; private set; } - - public bool Initialize(LogFactory factory) - { - Logger = factory.GetCurrentClassLogger(); +namespace NSSLServer.Plugin.Webapp; - return true; - } +public class Plugin : IPlugin +{ + /// + public string Name { get; } = "WebappPlugin"; - void IPlugin.ConfigureServices(Microsoft.Extensions.DependencyInjection.IServiceCollection services) - { - } + /// + public void Configure(WebApplication app) + { + var path = Path.Combine(app.Environment.ContentRootPath, "Static", "web"); + Directory.CreateDirectory(path); - void IPlugin.Configure(IApplicationBuilder app, IWebHostEnvironment environment) + app.UseFileServer(new FileServerOptions() { - app.UseFileServer(new FileServerOptions() - { - FileProvider = new PhysicalFileProvider( - Path.Combine(environment.ContentRootPath, "Static", "web")), - RequestPath = new PathString("/webapp") - }); - } - + FileProvider = new PhysicalFileProvider(path), + RequestPath = new PathString("/webapp") + }); } } \ No newline at end of file diff --git a/NSSLServer.sln b/NSSLServer.sln index e917c08..20d388f 100644 --- a/NSSLServer.sln +++ b/NSSLServer.sln @@ -6,6 +6,7 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{65527620-5DCC-4BD5-9D5E-E00E34858168}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + docker-compose.yml = docker-compose.yml EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NSSLServer.Runtime", "NSSLServer\NSSLServer.Runtime.csproj", "{492180CB-E065-4E9B-A7CD-9B6CA3BA1FA4}" diff --git a/NSSLServer/Dockerfile b/NSSLServer/Dockerfile index ec4ab6b..7e97a14 100644 --- a/NSSLServer/Dockerfile +++ b/NSSLServer/Dockerfile @@ -2,7 +2,8 @@ FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base WORKDIR /app -EXPOSE 80 +EXPOSE 8080 +EXPOSE 8081 FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build WORKDIR /src diff --git a/NSSLServer/NSSLServer.Runtime.csproj b/NSSLServer/NSSLServer.Runtime.csproj index 03adb73..428d437 100644 --- a/NSSLServer/NSSLServer.Runtime.csproj +++ b/NSSLServer/NSSLServer.Runtime.csproj @@ -11,6 +11,7 @@ True false enable + enable @@ -24,13 +25,14 @@ - - - - - + + + - + + + + @@ -40,8 +42,12 @@ + + + + diff --git a/NSSLServer/PluginLoader.cs b/NSSLServer/PluginLoader.cs index 739d8a4..c5b81e2 100644 --- a/NSSLServer/PluginLoader.cs +++ b/NSSLServer/PluginLoader.cs @@ -1,159 +1,163 @@ -using NLog; - -using NSSLServer.Core.Extension; +using NSSLServer.Core.Extension; using NSSLServer.Core.HelperMethods; using NSSLServer.Database.Updater; using System.Reflection; using System.Runtime.Loader; -namespace NSSLServer.Features +namespace NSSLServer.Features; + +public class PluginLoader { - public class PluginLoader + public List ControllerTypes { get; } = []; + + public List DbUpdater { get; } = []; + + private readonly List plugins = []; + private readonly ILogger logger; + + private readonly Assembly assembly; + private readonly string workdir; + + public PluginLoader(ILogger logger) { - public List ControllerTypes { get; } = new List(); - public List DbUpdater { get; } = new List(); + ArgumentNullException.ThrowIfNull(logger); - private List plugins = new List(); - private readonly NLog.ILogger logger; + this.logger = logger; + assembly = Assembly.GetExecutingAssembly(); + workdir = Path.GetDirectoryName(assembly.Location); + } - public PluginLoader(LogFactory logFactory) - { - logger = logFactory.GetCurrentClassLogger(); - } + internal void LoadPlugins(Assembly ass) + { + var allOfThemTypes = ass.GetTypes(); - internal void LoadPlugins(Assembly ass) + foreach (var type in allOfThemTypes) { - var allOfThemTypes = ass.GetTypes(); - - foreach (var type in allOfThemTypes) + if (typeof(IPlugin).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract) { - if (typeof(IPlugin).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract) - { - logger.Info($"Loading Plugin {type.Name} from Assembly {ass.FullName}"); - plugins.Add(PluginCreator.GetInstance(type)); - } - else if (typeof(BaseController).IsAssignableFrom(type)) - { - ControllerTypes.Add(type); - } - else if (typeof(IDbUpdater).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract) - { - var updater = PluginCreator.GetInstance(type); - DbUpdater.Add(updater); - } - + logger.LogInformation("Loading Plugin {typeName} from Assembly {assemblyName}", type.Name, ass.FullName); + plugins.Add(PluginCreator.GetInstance(type)); + } + // TODO: each plugin should add itself to the controllers + else if (typeof(BaseController).IsAssignableFrom(type)) + { + ControllerTypes.Add(type); + } + else if (typeof(IDbUpdater).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract) + { + var updater = PluginCreator.GetInstance(type, logger); + DbUpdater.Add(updater); } } + } - internal void InitializeConfigure(IApplicationBuilder app, IWebHostEnvironment environment) - { - foreach (var item in plugins) - item.Configure(app, environment); + internal void InitializeDbUpdater() + { + var updaterLoadTasks = new List(); + foreach (var updater in DbUpdater) + { + updater.LoadDesiredVersion(); + updaterLoadTasks.Add(updater.LoadCurrentVersion()); + updater.RegisterTypes(); } - internal void InitializeConfigureServices(IServiceCollection services) + foreach (var loadTasks in updaterLoadTasks) { - foreach (var item in plugins) - item.ConfigureServices(services); - + if (!loadTasks.IsCompleted) + { + loadTasks.Wait(); + loadTasks.Dispose(); + } } + } - internal void InitializeDbUpdater() + internal async Task RunDbUpdates() + { + foreach (var updater in DbUpdater.OrderBy(x => x.Priority)) { - var updaterLoadTasks = new List(); - foreach (var updater in DbUpdater) + try { - updater.LoadDesiredVersion(); - updaterLoadTasks.Add(updater.LoadCurrentVersion()); - updater.RegisterTypes(); + await updater.RunUpdates(); } - - foreach (var loadTasks in updaterLoadTasks) + catch (Exception ex) { - - if (!loadTasks.IsCompleted) - { - loadTasks.Wait(); - loadTasks.Dispose(); - } } } + } - internal async Task RunDbUpdates() + /// + /// Loads all assemblies and initializes the plugins. + /// + public void LoadAssemblies() + { + var pluginDir = Path.Combine(workdir, "plugins"); + + if (Directory.Exists(pluginDir)) { - foreach (var updater in DbUpdater.OrderBy(x => x.Priority)) + var toRemove = new FileInfo(Path.Combine(pluginDir, "ToRemove.txt")); + if (toRemove.Exists) { - try - { - await updater.RunUpdates(); - } - catch (Exception ex) + foreach (var path in File.ReadAllLines(toRemove.FullName)) { + logger.LogInformation("Deleting existing file {path}", path); + File.Delete(path); } } - } - public void LoadAssemblies() - { - var workdir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + var allFiles = Directory.GetFiles(pluginDir); + var plugins = allFiles.Where(x => x.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase)).ToArray(); + var thirdParty = allFiles.Except(plugins); - if (Directory.Exists(Path.Combine(workdir, "plugins"))) + foreach (var plugin in plugins) { - var toRemove = new FileInfo(Path.Combine(workdir, "plugins", "ToRemove.txt")); - if (toRemove.Exists) - { - foreach (var path in File.ReadAllLines(toRemove.FullName)) - { - logger.Info($"Deleting existing file {path}"); - File.Delete(path); - } - } - - var plugins = Directory.GetFiles(Path.Combine(workdir, "plugins"), "*.dll"); - foreach (var plugin in plugins) - { - var filename = Path.GetFileName(plugin); - logger.Info($"Copying Plugin Assembly {filename}"); + var filename = Path.GetFileName(plugin); + logger.LogInformation("Copying Plugin Assembly {filename}", filename); - File.Copy(plugin, Path.Combine(workdir, filename), true); - } - - var thirdParty = Directory - .GetFiles(Path.Combine(workdir, "plugins")) - .Where(x => !x.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase)); + File.Copy(plugin, Path.Combine(workdir, filename), true); + } - foreach (var plugin in thirdParty) - { - var filename = Path.GetFileName(plugin); - logger.Info($"Copying Third Party File {filename}"); + foreach (var plugin in thirdParty) + { + var filename = Path.GetFileName(plugin); + logger.LogInformation("Copying Third Party File {filename}", filename); - File.Copy(plugin, Path.Combine(workdir, filename), true); - } + File.Copy(plugin, Path.Combine(workdir, filename), true); } + } + var paths = Directory.GetFiles(workdir, "*.dll"); - var paths = Directory.GetFiles(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "*.dll"); - - foreach (var path in paths) + foreach (var path in paths) + { + var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(path); + if (assembly.GetCustomAttribute() is not null) { - var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(path); - if (assembly.GetCustomAttribute() is not null) - { - logger.Info($"Loading Plugins from Assembly {assembly.FullName}"); - LoadPlugins(assembly); - } + logger.LogInformation("Loading Plugins from Assembly {name}", assembly.FullName); + LoadPlugins(assembly); } } + } - public void InitializePlugins(LogFactory logFactory) + /// + /// Configures the plugins during application startup. + /// + public void ConfigurePlugins(WebApplicationBuilder builder) + { + foreach (var plugin in plugins) { - foreach (var plugin in plugins) - { - if (!plugin.Initialize(logFactory)) - logger.Warn($"Plugin {plugin.Name} had errors in initialization :("); - } + plugin.Configure(builder); + } + } + /// + /// Configures the plugins after all services are registered. + /// + public void ConfigurePlugins(WebApplication app) + { + foreach (var plugin in plugins) + { + plugin.Configure(app); } } } diff --git a/NSSLServer/Program.cs b/NSSLServer/Program.cs index 0995e85..cb3e2cd 100644 --- a/NSSLServer/Program.cs +++ b/NSSLServer/Program.cs @@ -1,53 +1,151 @@ -using NLog.Web; - +using Microsoft.AspNetCore.Mvc.ApplicationParts; +using Microsoft.AspNetCore.ResponseCompression; +using Microsoft.OpenApi.Models; using NSSLServer.Features; +using Serilog; + +ThreadPool.SetMaxThreads(500, 500); -namespace NSSLServer +var builder = WebApplication.CreateBuilder(new WebApplicationOptions() { - public class Program - { - internal static PluginLoader PluginLoader; + Args = args, + ContentRootPath = Directory.GetCurrentDirectory(), +}); + +// Two-step initialization bootstrapping Serilog +// https://github.com/serilog/serilog-aspnetcore?tab=readme-ov-file#two-stage-initialization +Log.Logger = new LoggerConfiguration() + .WriteTo.Console() + .CreateBootstrapLogger(); + +using var factory = new Serilog.Extensions.Logging.SerilogLoggerFactory(); + +builder.Host.UseSerilog((context, services, configuration) => configuration + .ReadFrom.Configuration(context.Configuration) + .ReadFrom.Services(services) + .Enrich.FromLogContext()); + +var PluginLoader = new PluginLoader(factory.CreateLogger>()); +PluginLoader.LoadAssemblies(); +PluginLoader.InitializeDbUpdater(); + +await PluginLoader.RunDbUpdates(); + +builder.Configuration +.SetBasePath(builder.Environment.ContentRootPath) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true); -#if DEBUG - private const int Port = 4344; -#else - private const int Port = 80; -#endif +builder.Configuration.AddEnvironmentVariables(); - public static async Task Main(string[] args) +var pluginWithControllerAssemblies = PluginLoader + .ControllerTypes + .Select(x => x.Assembly) + .Distinct() + .ToArray(); + +var services = builder.Services; +services.AddCors(); +services + .AddMvc() + .ConfigureApplicationPartManager(manager => + { + foreach (var assembly in pluginWithControllerAssemblies) { - var logFactory = NLogBuilder.ConfigureNLog("nlog.config"); - ThreadPool.SetMaxThreads(500, 500); + var partFactory = ApplicationPartFactory.GetApplicationPartFactory(assembly); + foreach (var applicationPart in partFactory.GetApplicationParts(assembly)) + { + manager.ApplicationParts.Add(applicationPart); + } + } + }); +services.AddControllers().AddNewtonsoftJson(); +services.AddResponseCompression(options => +{ + options.Providers.Add(); +}); +services.Configure(conf => conf.Level = System.IO.Compression.CompressionLevel.Optimal); +services.AddResponseCompression(); +services.AddHttpClient(); - PluginLoader = new PluginLoader(logFactory); - PluginLoader.LoadAssemblies(); +services.AddSwaggerGen(configuration => +{ + // Try get all documentation .xml files from plugins with controllers + foreach (var pluginWithControllerAssembly in pluginWithControllerAssemblies) + { + // Check plugin exists + var fileInfo = new FileInfo(pluginWithControllerAssembly.Location); + if (!fileInfo.Exists) + continue; - PluginLoader.InitializePlugins(logFactory); + // Check documentation file exists + var file = fileInfo.FullName; + var docu = new FileInfo($"{file[..file.LastIndexOf(fileInfo.Extension)]}.xml"); - PluginLoader.InitializeDbUpdater(); + if (!docu.Exists) + continue; - await PluginLoader.RunDbUpdates(); + configuration.IncludeXmlComments(docu.FullName, true); + } - var builder = WebApplication.CreateBuilder(new WebApplicationOptions() + configuration.SwaggerDoc("v1", new OpenApiInfo { Title = "NSSL API", Version = "v1" }); + configuration.AddSecurityDefinition("X-Token", new OpenApiSecurityScheme() + { + Name = "X-Token", + Scheme = "string", + Type = SecuritySchemeType.ApiKey, + In = ParameterLocation.Header, + }); + + configuration.AddSecurityRequirement(new OpenApiSecurityRequirement() + { + { + new OpenApiSecurityScheme() { - Args = args, - ContentRootPath = Directory.GetCurrentDirectory(), - }); - - builder.Logging.AddConsole(); - builder.Logging.AddDebug(); - builder.WebHost.UseKestrel(ks => ks.ListenAnyIP(Port)); - builder.WebHost.UseNLog(); - - var startup = new Startup(builder.Configuration, builder.Environment); - startup.ConfigureServices(builder.Services); - PluginLoader.InitializeConfigureServices(builder.Services); - - var app = builder.Build(); - startup.Configure(app, app.Environment); - PluginLoader.InitializeConfigure(app, app.Environment); - app.Run(); - + Reference = new OpenApiReference() + { + Id = "X-Token", + Type = ReferenceType.SecurityScheme, + } + }, + Array.Empty() } + }); +}); + +PluginLoader.ConfigurePlugins(builder); + +var app = builder.Build(); + +app.UseCors(o => o.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()); +app.UseSwagger(); +app.UseSwaggerUI(configuration => +{ + configuration.SwaggerEndpoint("/swagger/v1/swagger.json", "NSSL API V1"); +}); + +app.UseResponseCompression(); +app.UseStaticFiles(); +app.UseRouting(); +app.Use(async (ctx, f) => +{ + ctx.Response.Headers.AccessControlAllowOrigin = "*";// ctx.Request.Headers.TryGetValue("Origin", out StringValues originValues) ? originValues[0] : "*"; + ctx.Response.Headers.AccessControlAllowCredentials = "true"; + if (ctx.Request.Method == "OPTIONS") + { + ctx.Response.Headers.AccessControlAllowMethods = "GET, POST, PUT, DELETE"; + ctx.Response.Headers.AccessControlAllowHeaders = "Origin, X-Token, X-Requested-With, Content-Type, Accept"; + ctx.Response.Headers.AccessControlMaxAge = "1728000"; + } + else + { + await f(); } -} +}); + +app.MapControllerRoute("default", "{controller=Home}/{action=Index}"); + +PluginLoader.ConfigurePlugins(app); +app.Run(); + +Log.CloseAndFlush(); diff --git a/NSSLServer/Properties/launchSettings.json b/NSSLServer/Properties/launchSettings.json index 5b7e4d6..7a31cc1 100644 --- a/NSSLServer/Properties/launchSettings.json +++ b/NSSLServer/Properties/launchSettings.json @@ -1,34 +1,35 @@ { - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:49477/", - "sslPort": 0 - } - }, "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "api/values", + "http": { + "commandName": "Project", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - } + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5000", + "launchBrowser": true, + "launchUrl": "swagger" }, - "NSSLServer": { + "https": { "commandName": "Project", - "launchUrl": "http://localhost:5000/api/values", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - } + }, + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:7143;http://localhost:5000", + "launchBrowser": true, + "launchUrl": "swagger" }, - "Docker": { + "Container (Dockerfile)": { "commandName": "Docker", - "launchBrowser": true, - "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/api/values", - "environmentVariables": {}, - "httpPort": 49478 + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", + "environmentVariables": { + "ASPNETCORE_HTTPS_PORTS": "8081", + "ASPNETCORE_HTTP_PORTS": "8080" + }, + "publishAllPorts": true, + "useSSL": true } - } + }, + "$schema": "https://json.schemastore.org/launchsettings.json" } \ No newline at end of file diff --git a/NSSLServer/Startup.cs b/NSSLServer/Startup.cs deleted file mode 100644 index 6076c00..0000000 --- a/NSSLServer/Startup.cs +++ /dev/null @@ -1,140 +0,0 @@ -using Microsoft.AspNetCore.Mvc.ApplicationParts; -using Microsoft.AspNetCore.ResponseCompression; -using Microsoft.OpenApi.Models; - -namespace NSSLServer -{ - public class Startup - { - public IConfiguration Configuration { get; } - - public Startup(IConfiguration configuration, IWebHostEnvironment env) - { - Configuration = configuration; - - var builder = new ConfigurationBuilder() - .SetBasePath(env.ContentRootPath) - .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); - - - builder.AddEnvironmentVariables(); - builder.Build(); - } - - public void ConfigureServices(IServiceCollection services) - { - var pluginWithControllerAssemblies = Program - .PluginLoader - .ControllerTypes - .Select(x => x.Assembly) - .Distinct() - .ToArray(); - - services.AddCors(); - services - .AddMvc() - .ConfigureApplicationPartManager(manager => - { - foreach (var assembly in pluginWithControllerAssemblies) - { - var partFactory = ApplicationPartFactory.GetApplicationPartFactory(assembly); - foreach (var applicationPart in partFactory.GetApplicationParts(assembly)) - { - manager.ApplicationParts.Add(applicationPart); - } - } - }); - services.AddControllers().AddNewtonsoftJson(); - services.AddResponseCompression(options => - { - options.Providers.Add(); - }); - services.Configure(conf => conf.Level = System.IO.Compression.CompressionLevel.Optimal); - services.AddResponseCompression(); - services.AddHttpClient(); - - services.AddSwaggerGen(configuration => - { - // Try get all documentation .xml files from plugins with controllers - foreach (var pluginWithControllerAssembly in pluginWithControllerAssemblies) - { - // Check plugin exists - var fileInfo = new FileInfo(pluginWithControllerAssembly.Location); - if (!fileInfo.Exists) - continue; - - // Check documentation file exists - var file = fileInfo.FullName; - var docu = new FileInfo($"{file[..file.LastIndexOf(fileInfo.Extension)]}.xml"); - - if (!docu.Exists) - continue; - - configuration.IncludeXmlComments(docu.FullName, true); - } - - configuration.SwaggerDoc("v1", new OpenApiInfo { Title = "NSSL API", Version = "v1" }); - configuration.AddSecurityDefinition("X-Token", new OpenApiSecurityScheme() - { - Name = "X-Token", - Scheme = "string", - Type = SecuritySchemeType.ApiKey, - In = ParameterLocation.Header, - }); - - configuration.AddSecurityRequirement(new OpenApiSecurityRequirement() - { - { - new OpenApiSecurityScheme() - { - Reference = new OpenApiReference() - { - Id = "X-Token", - Type = ReferenceType.SecurityScheme, - } - }, - Array.Empty() - } - }); - }); - - } - - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - app.UseCors(o => o.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()); - app.UseSwagger(); - app.UseSwaggerUI(configuration => - { - configuration.SwaggerEndpoint("/swagger/v1/swagger.json", "NSSL API V1"); - }); - - app.UseResponseCompression(); - app.UseStaticFiles(); - app.UseRouting(); - app.Use(async (ctx, f) => - { - ctx.Response.Headers["Access-Control-Allow-Origin"] = "*";// ctx.Request.Headers.TryGetValue("Origin", out StringValues originValues) ? originValues[0] : "*"; - ctx.Response.Headers["Access-Control-Allow-Credentials"] = "true"; - if (ctx.Request.Method == "OPTIONS") - { - ctx.Response.Headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE"; - ctx.Response.Headers["Access-Control-Allow-Headers"] = "Origin, X-Token, X-Requested-With, Content-Type, Accept"; - ctx.Response.Headers["Access-Control-Max-Age"] = "1728000"; - } - else - { - await f(); - } - }); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllerRoute( - "default", - "{controller=Home}/{action=Index}"); - }); - } - } -} diff --git a/NSSLServer/appsettings.Development.json b/NSSLServer/appsettings.Development.json new file mode 100644 index 0000000..55a4a10 --- /dev/null +++ b/NSSLServer/appsettings.Development.json @@ -0,0 +1,32 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "Serilog": { + "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ], + "MinimumLevel": { + "Default": "Debug", + "Override": { + "Microsoft.EntityFrameworkCore": "Warning", + "Experimental": "Fatal" + } + }, + "WriteTo": [ + { + "Name": "Console" + }, + { + "Name": "File", + "Args": { + "path": "external/logs/log.txt", + "rollingInterval": "Day", + "retainedFileCountLimit": 7 + } + } + ] + } +} diff --git a/NSSLServer/nlog.config b/NSSLServer/nlog.config deleted file mode 100644 index 8ed7c3b..0000000 --- a/NSSLServer/nlog.config +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..a34ac53 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,18 @@ +services: + postgres: + image: postgres + restart: always + environment: + POSTGRES_PASSWORD: password + volumes: + - postgres_storage:/var/lib/postgresql + ports: + - "5432:5432" + healthcheck: + test: [ "CMD-SHELL", "pg_isready -U postgres" ] + interval: 5s + timeout: 5s + retries: 5 + +volumes: + postgres_storage: \ No newline at end of file