diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 2a8688fc..00000000 --- a/.dockerignore +++ /dev/null @@ -1,30 +0,0 @@ -**/.dockerignore -**/.env -**/.git -**/.gitignore -**/.project -**/.settings -**/.toolstarget -**/.vs -**/.vscode -**/.idea -**/*.*proj.user -**/*.dbmdl -**/*.jfm -**/azds.yaml -**/bin -**/charts -**/docker-compose* -**/Dockerfile* -**/node_modules -**/npm-debug.log -**/obj -**/secrets.dev.yaml -**/values.dev.yaml -**/storage -**/compose.yml -LICENSE -README.md - -# For the people who run moonlight inside the main repo with the relative data path -data diff --git a/Moonlight.ApiServer.Runtime/Program.cs b/Moonlight.ApiServer.Runtime/Program.cs index 619c343a..237a59af 100644 --- a/Moonlight.ApiServer.Runtime/Program.cs +++ b/Moonlight.ApiServer.Runtime/Program.cs @@ -1,9 +1,35 @@ -using Moonlight.ApiServer; -using Moonlight.ApiServer.Runtime; - -var startup = new Startup(); +using Moonlight.ApiServer.Runtime; +using Moonlight.ApiServer.Startup; var pluginLoader = new PluginLoader(); pluginLoader.Initialize(); +/* +await startup.Run(args, pluginLoader.Instances); -await startup.Run(args, pluginLoader.Instances); \ No newline at end of file +*/ + +var cs = new Startup(); + +await cs.Initialize(args, pluginLoader.Instances); + +var builder = WebApplication.CreateBuilder(args); + +await cs.AddMoonlight(builder); + +var app = builder.Build(); + +await cs.AddMoonlight(app); + +// Handle setup of wasm app hosting in the runtime +// so the Moonlight.ApiServer doesn't need the wasm package +if (cs.Configuration.Frontend.EnableHosting) +{ + if (app.Environment.IsDevelopment()) + app.UseWebAssemblyDebugging(); + + app.UseBlazorFrameworkFiles(); + app.UseStaticFiles(); +} + + +await app.RunAsync(); \ No newline at end of file diff --git a/Moonlight.ApiServer/Configuration/AppConfiguration.cs b/Moonlight.ApiServer/Configuration/AppConfiguration.cs index c6b2ffe5..3691cfb2 100644 --- a/Moonlight.ApiServer/Configuration/AppConfiguration.cs +++ b/Moonlight.ApiServer/Configuration/AppConfiguration.cs @@ -1,24 +1,45 @@ using MoonCore.Helpers; +using YamlDotNet.Serialization; namespace Moonlight.ApiServer.Configuration; -public class AppConfiguration +public record AppConfiguration { + [YamlMember(Description = "The public url your instance should be accessible through")] public string PublicUrl { get; set; } = "http://localhost:5165"; + [YamlMember(Description = "The credentials of the postgres which moonlight should use")] public DatabaseConfig Database { get; set; } = new(); + + [YamlMember(Description = "Settings regarding authentication")] public AuthenticationConfig Authentication { get; set; } = new(); + + [YamlMember(Description = "These options are only meant for development purposes")] public DevelopmentConfig Development { get; set; } = new(); - public ClientConfig Client { get; set; } = new(); + public FrontendData Frontend { get; set; } = new(); public KestrelConfig Kestrel { get; set; } = new(); public MetricsData Metrics { get; set; } = new(); - public class ClientConfig + public static AppConfiguration CreateEmpty() { - public bool Enable { get; set; } = true; + return new AppConfiguration() + { + // Set arrays as empty here + + Kestrel = new() + { + AllowedOrigins = [] + } + }; + } + + public record FrontendData + { + [YamlMember(Description = "Enable the hosting of the frontend. Disable this if you only want to run the api server")] + public bool EnableHosting { get; set; } = true; } - public class DatabaseConfig + public record DatabaseConfig { public string Host { get; set; } = "your-database-host.name"; public int Port { get; set; } = 5432; @@ -29,15 +50,19 @@ public class AppConfiguration public string Database { get; set; } = "db_name"; } - public class AuthenticationConfig + public record AuthenticationConfig { + [YamlMember(Description = "The secret token to use for creating jwts and encrypting things. This needs to be at least 32 characters long")] public string Secret { get; set; } = Formatter.GenerateString(32); + + [YamlMember(Description = "The lifespan of generated user tokens in hours")] public int TokenDuration { get; set; } = 24 * 10; + [YamlMember(Description = "This enables the use of the local oauth2 provider, so moonlight will use itself as an oauth2 provider")] public bool EnableLocalOAuth2 { get; set; } = true; public OAuth2Data OAuth2 { get; set; } = new(); - public class OAuth2Data + public record OAuth2Data { public string Secret { get; set; } = Formatter.GenerateString(32); public string ClientId { get; set; } = Formatter.GenerateString(8); @@ -46,24 +71,32 @@ public class AppConfiguration public string? AccessEndpoint { get; set; } public string? AuthorizationRedirect { get; set; } + [YamlMember(Description = "This specifies if the first registered user will become an admin automatically. This only works when using local oauth2")] public bool FirstUserAdmin { get; set; } = true; } } - public class DevelopmentConfig + public record DevelopmentConfig { + [YamlMember(Description = "This toggles the availability of the api docs via /api/swagger")] public bool EnableApiDocs { get; set; } = false; } - public class KestrelConfig + public record KestrelConfig { + [YamlMember(Description = "The upload limit in megabytes for the api server")] public int UploadLimit { get; set; } = 100; - public string AllowedOrigins { get; set; } = "*"; + + [YamlMember(Description = "The allowed origins for the api server. Use * to allow all origins (which is not advised)")] + public string[] AllowedOrigins { get; set; } = ["*"]; } - public class MetricsData + public record MetricsData { + [YamlMember(Description = "This enables the collecting of metrics and allows access to the /metrics endpoint")] public bool Enable { get; set; } = false; + + [YamlMember(Description = "The interval in which metrics are created, specified in seconds")] public int Interval { get; set; } = 15; } } \ No newline at end of file diff --git a/Moonlight.ApiServer/Database/Entities/ApiKey.cs b/Moonlight.ApiServer/Database/Entities/ApiKey.cs index 3dbfb9c8..9b75d2ec 100644 --- a/Moonlight.ApiServer/Database/Entities/ApiKey.cs +++ b/Moonlight.ApiServer/Database/Entities/ApiKey.cs @@ -1,6 +1,4 @@ -using System.ComponentModel.DataAnnotations.Schema; - -namespace Moonlight.ApiServer.Database.Entities; +namespace Moonlight.ApiServer.Database.Entities; public class ApiKey { diff --git a/Moonlight.ApiServer/Database/Entities/User.cs b/Moonlight.ApiServer/Database/Entities/User.cs index 0a13fafb..4a09c19d 100644 --- a/Moonlight.ApiServer/Database/Entities/User.cs +++ b/Moonlight.ApiServer/Database/Entities/User.cs @@ -1,6 +1,4 @@ -using System.ComponentModel.DataAnnotations.Schema; - -namespace Moonlight.ApiServer.Database.Entities; +namespace Moonlight.ApiServer.Database.Entities; public class User { diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/AdvancedController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/AdvancedController.cs index 1184da4c..3873564f 100644 --- a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/AdvancedController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/AdvancedController.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Moonlight.ApiServer.Services; diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/DiagnoseController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/DiagnoseController.cs index fb4d3a58..8b2bbc51 100644 --- a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/DiagnoseController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/DiagnoseController.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Moonlight.ApiServer.Services; using Moonlight.Shared.Http.Requests.Admin.Sys; diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/ThemeController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/ThemeController.cs index 1ec69246..f065d97d 100644 --- a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/ThemeController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/ThemeController.cs @@ -1,7 +1,6 @@ using System.Text.Json; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using MoonCore.Helpers; using Moonlight.Shared.Http.Requests.Admin.Sys; namespace Moonlight.ApiServer.Http.Controllers.Admin.Sys; diff --git a/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs b/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs index 61de0a4f..aee4328d 100644 --- a/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs @@ -1,19 +1,17 @@ using System.IdentityModel.Tokens.Jwt; using System.Text; -using System.Text.Json; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Tokens; using MoonCore.Exceptions; using MoonCore.Extended.Abstractions; -using MoonCore.Helpers; using Moonlight.ApiServer.Configuration; using Moonlight.ApiServer.Database.Entities; using Moonlight.ApiServer.Interfaces; using Moonlight.Shared.Http.Requests.Auth; using Moonlight.Shared.Http.Responses.Auth; -using Moonlight.Shared.Http.Responses.OAuth2; namespace Moonlight.ApiServer.Http.Controllers.Auth; @@ -76,7 +74,7 @@ public class AuthController : Controller // Generate token var securityTokenDescriptor = new SecurityTokenDescriptor() { - Expires = DateTime.Now.AddYears(Configuration.Authentication.TokenDuration), + Expires = DateTime.Now.AddHours(Configuration.Authentication.TokenDuration), IssuedAt = DateTime.Now, NotBefore = DateTime.Now.AddMinutes(-1), Claims = new Dictionary() diff --git a/Moonlight.ApiServer/Http/Controllers/Frontend/FrontendController.cs b/Moonlight.ApiServer/Http/Controllers/Frontend/FrontendController.cs index 028971c6..9271af67 100644 --- a/Moonlight.ApiServer/Http/Controllers/Frontend/FrontendController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Frontend/FrontendController.cs @@ -1,4 +1,5 @@ using System.Text; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Moonlight.ApiServer.Services; using Moonlight.Shared.Misc; diff --git a/Moonlight.ApiServer/Http/Controllers/Frontend/FrontendPage.razor b/Moonlight.ApiServer/Http/Controllers/Frontend/FrontendPage.razor index f9f7adc3..0832de32 100644 --- a/Moonlight.ApiServer/Http/Controllers/Frontend/FrontendPage.razor +++ b/Moonlight.ApiServer/Http/Controllers/Frontend/FrontendPage.razor @@ -14,8 +14,8 @@ } - - + + diff --git a/Moonlight.ApiServer/Http/Controllers/OAuth2/OAuth2Controller.cs b/Moonlight.ApiServer/Http/Controllers/OAuth2/OAuth2Controller.cs index 50d359aa..05c74a28 100644 --- a/Moonlight.ApiServer/Http/Controllers/OAuth2/OAuth2Controller.cs +++ b/Moonlight.ApiServer/Http/Controllers/OAuth2/OAuth2Controller.cs @@ -4,6 +4,7 @@ using System.Security.Claims; using System.Text; using System.Text.RegularExpressions; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; @@ -55,11 +56,11 @@ public partial class OAuth2Controller : Controller throw new HttpApiException("Invalid oauth2 request", 400); } - Response.StatusCode = 200; + string html; if (view == "register") { - var html = await ComponentHelper.RenderComponent(HttpContext.RequestServices, parameters => + html = await ComponentHelper.RenderComponent(HttpContext.RequestServices, parameters => { parameters.Add("ClientId", clientId); parameters.Add("RedirectUri", redirectUri); @@ -70,7 +71,7 @@ public partial class OAuth2Controller : Controller } else { - var html = await ComponentHelper.RenderComponent(HttpContext.RequestServices, parameters => + html = await ComponentHelper.RenderComponent(HttpContext.RequestServices, parameters => { parameters.Add("ClientId", clientId); parameters.Add("RedirectUri", redirectUri); @@ -79,6 +80,10 @@ public partial class OAuth2Controller : Controller await Response.WriteAsync(html); } + + await Results + .Text(html, "text/html") + .ExecuteAsync(HttpContext); } [AllowAnonymous] @@ -116,7 +121,6 @@ public partial class OAuth2Controller : Controller var code = await GenerateCode(user); Response.Redirect($"{redirectUri}?code={code}"); - return; } else { @@ -124,39 +128,38 @@ public partial class OAuth2Controller : Controller var code = await GenerateCode(user); Response.Redirect($"{redirectUri}?code={code}"); - return; } } catch (HttpApiException e) { errorMessage = e.Title; - } - - Response.StatusCode = 200; - - if (view == "register") - { - var html = await ComponentHelper.RenderComponent(HttpContext.RequestServices, parameters => + + string html; + + if (view == "register") { - parameters.Add("ClientId", clientId); - parameters.Add("RedirectUri", redirectUri); - parameters.Add("ResponseType", responseType); - parameters.Add("ErrorMessage", errorMessage!); - }); - - await Response.WriteAsync(html); - } - else - { - var html = await ComponentHelper.RenderComponent(HttpContext.RequestServices, parameters => + html = await ComponentHelper.RenderComponent(HttpContext.RequestServices, parameters => + { + parameters.Add("ClientId", clientId); + parameters.Add("RedirectUri", redirectUri); + parameters.Add("ResponseType", responseType); + parameters.Add("ErrorMessage", errorMessage!); + }); + } + else { - parameters.Add("ClientId", clientId); - parameters.Add("RedirectUri", redirectUri); - parameters.Add("ResponseType", responseType); - parameters.Add("ErrorMessage", errorMessage!); - }); + html = await ComponentHelper.RenderComponent(HttpContext.RequestServices, parameters => + { + parameters.Add("ClientId", clientId); + parameters.Add("RedirectUri", redirectUri); + parameters.Add("ResponseType", responseType); + parameters.Add("ErrorMessage", errorMessage!); + }); + } - await Response.WriteAsync(html); + await Results + .Text(html, "text/html") + .ExecuteAsync(HttpContext); } } diff --git a/Moonlight.ApiServer/Implementations/LocalOAuth2Provider.cs b/Moonlight.ApiServer/Implementations/LocalOAuth2Provider.cs index 98a12200..3586e74a 100644 --- a/Moonlight.ApiServer/Implementations/LocalOAuth2Provider.cs +++ b/Moonlight.ApiServer/Implementations/LocalOAuth2Provider.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; using MoonCore.Exceptions; using MoonCore.Extended.Abstractions; using MoonCore.Helpers; diff --git a/Moonlight.ApiServer/Implementations/Metrics/ApplicationMetric.cs b/Moonlight.ApiServer/Implementations/Metrics/ApplicationMetric.cs index 424910a9..acb2dddb 100644 --- a/Moonlight.ApiServer/Implementations/Metrics/ApplicationMetric.cs +++ b/Moonlight.ApiServer/Implementations/Metrics/ApplicationMetric.cs @@ -1,4 +1,5 @@ using System.Diagnostics.Metrics; +using Microsoft.Extensions.DependencyInjection; using Moonlight.ApiServer.Interfaces; using Moonlight.ApiServer.Services; diff --git a/Moonlight.ApiServer/Implementations/Metrics/UsersMetric.cs b/Moonlight.ApiServer/Implementations/Metrics/UsersMetric.cs index 698c4e8e..65bad6f1 100644 --- a/Moonlight.ApiServer/Implementations/Metrics/UsersMetric.cs +++ b/Moonlight.ApiServer/Implementations/Metrics/UsersMetric.cs @@ -1,5 +1,6 @@ using System.Diagnostics.Metrics; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; using MoonCore.Extended.Abstractions; using Moonlight.ApiServer.Database.Entities; using Moonlight.ApiServer.Interfaces; diff --git a/Moonlight.ApiServer/Implementations/Startup/CoreStartup.cs b/Moonlight.ApiServer/Implementations/Startup/CoreStartup.cs index f041be1b..54013d68 100644 --- a/Moonlight.ApiServer/Implementations/Startup/CoreStartup.cs +++ b/Moonlight.ApiServer/Implementations/Startup/CoreStartup.cs @@ -1,3 +1,7 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.OpenApi.Models; using Moonlight.ApiServer.Configuration; using Moonlight.ApiServer.Database; @@ -8,7 +12,6 @@ using Moonlight.ApiServer.Models; using Moonlight.ApiServer.Plugins; using Moonlight.ApiServer.Services; using OpenTelemetry.Metrics; -using OpenTelemetry.Trace; namespace Moonlight.ApiServer.Implementations.Startup; @@ -83,7 +86,7 @@ public class CoreStartup : IPluginStartup #region Client / Frontend - if (configuration.Client.Enable) + if (configuration.Frontend.EnableHosting) { builder.Services.AddSingleton(new FrontendConfigurationOption() { diff --git a/Moonlight.ApiServer/Moonlight.ApiServer.csproj b/Moonlight.ApiServer/Moonlight.ApiServer.csproj index 9209e3af..1e6bec96 100644 --- a/Moonlight.ApiServer/Moonlight.ApiServer.csproj +++ b/Moonlight.ApiServer/Moonlight.ApiServer.csproj @@ -1,5 +1,5 @@  - + net9.0 enable @@ -12,15 +12,9 @@ - - - .dockerignore - false - - Moonlight.ApiServer - 2.1.2 + 2.1.3 Moonlight Panel A build of the api server for moonlight development https://github.com/Moonlight-Panel/Moonlight @@ -32,7 +26,6 @@ - @@ -45,16 +38,6 @@ - - true - src - Never - - - true - src - Never - diff --git a/Moonlight.ApiServer/Plugins/IPluginStartup.cs b/Moonlight.ApiServer/Plugins/IPluginStartup.cs index 92eac50a..91995389 100644 --- a/Moonlight.ApiServer/Plugins/IPluginStartup.cs +++ b/Moonlight.ApiServer/Plugins/IPluginStartup.cs @@ -1,3 +1,7 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.Hosting; + namespace Moonlight.ApiServer.Plugins; public interface IPluginStartup diff --git a/Moonlight.ApiServer/Program.cs b/Moonlight.ApiServer/Program.cs deleted file mode 100644 index f872198a..00000000 --- a/Moonlight.ApiServer/Program.cs +++ /dev/null @@ -1,5 +0,0 @@ -using Moonlight.ApiServer; - -var startup = new Startup(); - -await startup.Run(args); \ No newline at end of file diff --git a/Moonlight.ApiServer/Services/ApiKeyService.cs b/Moonlight.ApiServer/Services/ApiKeyService.cs index 72411ff5..6910ae71 100644 --- a/Moonlight.ApiServer/Services/ApiKeyService.cs +++ b/Moonlight.ApiServer/Services/ApiKeyService.cs @@ -1,6 +1,5 @@ using System.IdentityModel.Tokens.Jwt; using System.Text; -using System.Text.Json; using Microsoft.IdentityModel.Tokens; using MoonCore.Attributes; using Moonlight.ApiServer.Configuration; diff --git a/Moonlight.ApiServer/Services/ApplicationService.cs b/Moonlight.ApiServer/Services/ApplicationService.cs index 35f117a9..37caef68 100644 --- a/Moonlight.ApiServer/Services/ApplicationService.cs +++ b/Moonlight.ApiServer/Services/ApplicationService.cs @@ -1,5 +1,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using MoonCore.Attributes; using MoonCore.Helpers; diff --git a/Moonlight.ApiServer/Services/DiagnoseService.cs b/Moonlight.ApiServer/Services/DiagnoseService.cs index a5844b04..51c9e04a 100644 --- a/Moonlight.ApiServer/Services/DiagnoseService.cs +++ b/Moonlight.ApiServer/Services/DiagnoseService.cs @@ -1,5 +1,6 @@ using Moonlight.ApiServer.Interfaces; using System.IO.Compression; +using Microsoft.Extensions.Logging; using MoonCore.Attributes; using MoonCore.Exceptions; using Moonlight.Shared.Http.Responses.Admin.Sys; diff --git a/Moonlight.ApiServer/Services/FrontendService.cs b/Moonlight.ApiServer/Services/FrontendService.cs index 0ae03fc5..e295f4bf 100644 --- a/Moonlight.ApiServer/Services/FrontendService.cs +++ b/Moonlight.ApiServer/Services/FrontendService.cs @@ -1,6 +1,7 @@ using System.IO.Compression; using System.Text; using System.Text.Json; +using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.FileProviders; using MoonCore.Attributes; using MoonCore.Exceptions; @@ -82,7 +83,7 @@ public class FrontendService public async Task GenerateZip() // TODO: Rework to be able to extract everything successfully { // We only allow the access to this function when we are actually hosting the frontend - if (!Configuration.Client.Enable) + if (!Configuration.Frontend.EnableHosting) throw new HttpApiException("The hosting of the wasm client has been disabled", 400); // Load and check wasm path diff --git a/Moonlight.ApiServer/Services/MetricsBackgroundService.cs b/Moonlight.ApiServer/Services/MetricsBackgroundService.cs index b1e3ec91..619c4a18 100644 --- a/Moonlight.ApiServer/Services/MetricsBackgroundService.cs +++ b/Moonlight.ApiServer/Services/MetricsBackgroundService.cs @@ -1,4 +1,7 @@ using System.Diagnostics.Metrics; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using Moonlight.ApiServer.Configuration; using Moonlight.ApiServer.Interfaces; diff --git a/Moonlight.ApiServer/Startup.cs b/Moonlight.ApiServer/Startup.cs deleted file mode 100644 index 4cd04911..00000000 --- a/Moonlight.ApiServer/Startup.cs +++ /dev/null @@ -1,547 +0,0 @@ -using System.Text; -using System.Text.Json; -using Hangfire; -using Hangfire.EntityFrameworkCore; -using Microsoft.AspNetCore.Cors.Infrastructure; -using Microsoft.EntityFrameworkCore; -using Microsoft.IdentityModel.Tokens; -using MoonCore.EnvConfiguration; -using MoonCore.Extended.Abstractions; -using MoonCore.Extended.Extensions; -using MoonCore.Extended.Helpers; -using MoonCore.Extended.JwtInvalidation; -using MoonCore.Extensions; -using MoonCore.Helpers; -using MoonCore.Logging; -using MoonCore.Permissions; -using Moonlight.ApiServer.Configuration; -using Moonlight.ApiServer.Database; -using Moonlight.ApiServer.Database.Entities; -using Moonlight.ApiServer.Implementations; -using Moonlight.ApiServer.Implementations.Startup; -using Moonlight.ApiServer.Interfaces; -using Moonlight.ApiServer.Plugins; - -namespace Moonlight.ApiServer; - -// Cry about it -#pragma warning disable ASP0000 - -public class Startup -{ - private string[] Args; - - // Logging - private ILoggerProvider[] LoggerProviders; - private ILoggerFactory LoggerFactory; - private ILogger Logger; - - // Configuration - private AppConfiguration Configuration; - private IConfigurationRoot ConfigurationRoot; - - // WebApplication Stuff - private WebApplication WebApplication; - private WebApplicationBuilder WebApplicationBuilder; - - // Plugin Loading - private IPluginStartup[] PluginStartups; - private IPluginStartup[] AdditionalPlugins; - private IServiceProvider PluginLoadServiceProvider; - - public async Task Run(string[] args, IPluginStartup[]? additionalPlugins = null) - { - Args = args; - AdditionalPlugins = additionalPlugins ?? []; - - await PrintVersion(); - - await CreateStorage(); - await SetupAppConfiguration(); - await SetupLogging(); - await InitializePlugins(); - - await CreateWebApplicationBuilder(); - - await ConfigureKestrel(); - await RegisterAppConfiguration(); - await RegisterLogging(); - await RegisterBase(); - await RegisterDatabase(); - await RegisterAuth(); - await RegisterCors(); - await RegisterHangfire(); - await HookPluginBuild(); - await RegisterPluginAssets(); - - await BuildWebApplication(); - - await PrepareDatabase(); - - await UseCors(); - await UseBase(); - await UseAuth(); - await UseHangfire(); - await HookPluginConfigure(); - - await MapBase(); - await HookPluginEndpoints(); - - await WebApplication.RunAsync(); - } - - private Task PrintVersion() - { - // Fancy start console output... yes very fancy :> - var rainbow = new Crayon.Rainbow(0.5); - foreach (var c in "Moonlight") - { - Console.Write( - rainbow - .Next() - .Bold() - .Text(c.ToString()) - ); - } - - Console.WriteLine(); - - return Task.CompletedTask; - } - - private Task CreateStorage() - { - Directory.CreateDirectory("storage"); - Directory.CreateDirectory(Path.Combine("storage", "logs")); - - return Task.CompletedTask; - } - - #region Base - - private Task RegisterBase() - { - WebApplicationBuilder.Services.AutoAddServices(); - WebApplicationBuilder.Services.AddHttpClient(); - - WebApplicationBuilder.Services.AddApiExceptionHandler(); - - // Add pre-existing services - WebApplicationBuilder.Services.AddSingleton(Configuration); - - // Configure controllers - var mvcBuilder = WebApplicationBuilder.Services.AddControllers(); - - // Add plugin assemblies as application parts - foreach (var pluginStartup in PluginStartups.Select(x => x.GetType().Assembly).Distinct()) - mvcBuilder.AddApplicationPart(pluginStartup.GetType().Assembly); - - return Task.CompletedTask; - } - - private Task UseBase() - { - WebApplication.UseRouting(); - WebApplication.UseExceptionHandler(); - - if (Configuration.Client.Enable) - { - if (WebApplication.Environment.IsDevelopment()) - WebApplication.UseWebAssemblyDebugging(); - - WebApplication.UseBlazorFrameworkFiles(); - WebApplication.UseStaticFiles(); - } - - return Task.CompletedTask; - } - - private Task MapBase() - { - WebApplication.MapControllers(); - - if (Configuration.Client.Enable) - WebApplication.MapFallbackToController("Index", "Frontend"); - - return Task.CompletedTask; - } - - private Task ConfigureKestrel() - { - WebApplicationBuilder.WebHost.ConfigureKestrel(kestrelOptions => - { - var maxUploadInBytes = ByteConverter - .FromMegaBytes(Configuration.Kestrel.UploadLimit) - .Bytes; - - kestrelOptions.Limits.MaxRequestBodySize = maxUploadInBytes; - }); - - return Task.CompletedTask; - } - - #endregion - - #region Plugin Loading - - private Task InitializePlugins() - { - // Create service provider for starting up - var serviceCollection = new ServiceCollection(); - - serviceCollection.AddSingleton(Configuration); - - serviceCollection.AddLogging(builder => - { - builder.ClearProviders(); - builder.AddAnsiConsole(); - }); - - PluginLoadServiceProvider = serviceCollection.BuildServiceProvider(); - - // Collect startups - var pluginStartups = new List(); - - pluginStartups.AddRange(AdditionalPlugins); // Used by the development server - - // Do NOT remove the following comment, as its used to place the plugin startup register calls - // MLBUILD_PLUGIN_STARTUP_HERE - - - PluginStartups = pluginStartups.ToArray(); - - return Task.CompletedTask; - } - - private Task RegisterPluginAssets() - { - return Task.CompletedTask; - } - - #region Hooks - - private async Task HookPluginBuild() - { - foreach (var pluginAppStartup in PluginStartups) - { - try - { - await pluginAppStartup.BuildApplication(PluginLoadServiceProvider, WebApplicationBuilder); - } - catch (Exception e) - { - Logger.LogError( - "An error occured while processing 'BuildApp' for '{name}': {e}", - pluginAppStartup.GetType().FullName, - e - ); - } - } - } - - private async Task HookPluginConfigure() - { - foreach (var pluginAppStartup in PluginStartups) - { - try - { - await pluginAppStartup.ConfigureApplication(PluginLoadServiceProvider, WebApplication); - } - catch (Exception e) - { - Logger.LogError( - "An error occured while processing 'ConfigureApp' for '{name}': {e}", - pluginAppStartup.GetType().FullName, - e - ); - } - } - } - - private async Task HookPluginEndpoints() - { - foreach (var pluginEndpointStartup in PluginStartups) - { - try - { - await pluginEndpointStartup.ConfigureEndpoints(PluginLoadServiceProvider, WebApplication); - } - catch (Exception e) - { - Logger.LogError( - "An error occured while processing 'ConfigureEndpoints' for '{name}': {e}", - pluginEndpointStartup.GetType().FullName, - e - ); - } - } - } - - #endregion - - #endregion - - #region Configurations - - private async Task SetupAppConfiguration() - { - // Configure configuration (wow) - var configurationBuilder = new ConfigurationBuilder(); - - // Ensure configuration file exists - var jsonFilePath = Path.Combine(Directory.GetCurrentDirectory(), "storage", "app.json"); - - if (!File.Exists(jsonFilePath)) - await File.WriteAllTextAsync(jsonFilePath, JsonSerializer.Serialize(new AppConfiguration())); - - configurationBuilder.AddJsonFile( - jsonFilePath - ); - - configurationBuilder.AddEnvironmentVariables(prefix: "MOONLIGHT_", separator: "_"); - - ConfigurationRoot = configurationBuilder.Build(); - - // Retrieve configuration - Configuration = ConfigurationRoot.Get()!; - } - - private Task RegisterAppConfiguration() - { - WebApplicationBuilder.Services.AddSingleton(Configuration); - return Task.CompletedTask; - } - - #endregion - - #region Web Application - - private Task CreateWebApplicationBuilder() - { - WebApplicationBuilder = WebApplication.CreateBuilder(Args); - return Task.CompletedTask; - } - - private Task BuildWebApplication() - { - WebApplication = WebApplicationBuilder.Build(); - return Task.CompletedTask; - } - - #endregion - - #region Logging - - private Task SetupLogging() - { - LoggerFactory = new LoggerFactory(); - LoggerFactory.AddAnsiConsole(); - - Logger = LoggerFactory.CreateLogger(); - - return Task.CompletedTask; - } - - private async Task RegisterLogging() - { - // Configure application logging - WebApplicationBuilder.Logging.ClearProviders(); - WebApplicationBuilder.Logging.AddAnsiConsole(); - WebApplicationBuilder.Logging.AddFile(Path.Combine("storage", "logs", "moonlight.log")); - - // Logging levels - var logConfigPath = Path.Combine("storage", "logConfig.json"); - - // Ensure logging config, add a default one is missing - if (!File.Exists(logConfigPath)) - { - var defaultLogLevels = new Dictionary - { - { "Default", "Information" }, - { "Microsoft.AspNetCore", "Warning" }, - { "System.Net.Http.HttpClient", "Warning" } - }; - - var logLevelsJson = JsonSerializer.Serialize(defaultLogLevels); - await File.WriteAllTextAsync(logConfigPath, logLevelsJson); - } - - // Add logging configuration - var logLevels = JsonSerializer.Deserialize>( - await File.ReadAllTextAsync(logConfigPath) - )!; - - foreach (var level in logLevels) - WebApplicationBuilder.Logging.AddFilter(level.Key, Enum.Parse(level.Value)); - - // Mute exception handler middleware - // https://github.com/dotnet/aspnetcore/issues/19740 - WebApplicationBuilder.Logging.AddFilter( - "Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware", - LogLevel.Critical - ); - - WebApplicationBuilder.Logging.AddFilter( - "Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware", - LogLevel.Critical - ); - } - - #endregion - - #region Database - - private Task RegisterDatabase() - { - WebApplicationBuilder.Services.AddDatabaseMappings(); - WebApplicationBuilder.Services.AddServiceCollectionAccessor(); - - WebApplicationBuilder.Services.AddScoped(typeof(DatabaseRepository<>)); - - return Task.CompletedTask; - } - - private async Task PrepareDatabase() - { - await WebApplication.Services.EnsureDatabaseMigrated(); - - WebApplication.Services.GenerateDatabaseMappings(); - } - - #endregion - - #region Authentication & Authorisation - - private Task RegisterAuth() - { - WebApplicationBuilder.Services - .AddAuthentication("coreAuthentication") - .AddJwtBearer("coreAuthentication", options => - { - options.TokenValidationParameters = new() - { - IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes( - Configuration.Authentication.Secret - )), - ValidateIssuerSigningKey = true, - ValidateLifetime = true, - ClockSkew = TimeSpan.Zero, - ValidateAudience = true, - ValidAudience = Configuration.PublicUrl, - ValidateIssuer = true, - ValidIssuer = Configuration.PublicUrl - }; - }); - - WebApplicationBuilder.Services.AddJwtBearerInvalidation("coreAuthentication"); - WebApplicationBuilder.Services.AddScoped(); - - WebApplicationBuilder.Services.AddAuthorization(); - - WebApplicationBuilder.Services.AddAuthorizationPermissions(options => - { - options.ClaimName = "permissions"; - options.Prefix = "permissions:"; - }); - - // Add local oauth2 provider if enabled - if (Configuration.Authentication.EnableLocalOAuth2) - WebApplicationBuilder.Services.AddScoped(); - - return Task.CompletedTask; - } - - private Task UseAuth() - { - WebApplication.UseAuthentication(); - - WebApplication.UseAuthorization(); - - return Task.CompletedTask; - } - - #endregion - - #region Cors - - private Task RegisterCors() - { - var allowedOrigins = Configuration.Kestrel.AllowedOrigins.Split(";", StringSplitOptions.RemoveEmptyEntries); - - WebApplicationBuilder.Services.AddCors(options => - { - var cors = new CorsPolicyBuilder(); - - if (allowedOrigins.Contains("*")) - { - cors.SetIsOriginAllowed(_ => true) - .AllowAnyMethod() - .AllowAnyHeader() - .AllowCredentials(); - } - else - { - cors.WithOrigins(allowedOrigins) - .AllowAnyHeader() - .AllowAnyMethod() - .AllowCredentials(); - } - - options.AddDefaultPolicy( - cors.Build() - ); - }); - - return Task.CompletedTask; - } - - private Task UseCors() - { - WebApplication.UseCors(); - - return Task.CompletedTask; - } - - #endregion - - #region Hangfire - - private Task RegisterHangfire() - { - WebApplicationBuilder.Services.AddHangfire((provider, configuration) => - { - configuration.SetDataCompatibilityLevel(CompatibilityLevel.Version_180); - configuration.UseSimpleAssemblyNameTypeSerializer(); - configuration.UseRecommendedSerializerSettings(); - configuration.UseEFCoreStorage(() => - { - var scope = provider.CreateScope(); - return scope.ServiceProvider.GetRequiredService(); - }, new EFCoreStorageOptions()); - }); - - WebApplicationBuilder.Services.AddHangfireServer(); - - WebApplicationBuilder.Logging.AddFilter( - "Hangfire.Server.BackgroundServerProcess", - LogLevel.Warning - ); - - WebApplicationBuilder.Logging.AddFilter( - "Hangfire.BackgroundJobServer", - LogLevel.Warning - ); - - return Task.CompletedTask; - } - - private Task UseHangfire() - { - if (WebApplication.Environment.IsDevelopment()) - WebApplication.UseHangfireDashboard(); - - return Task.CompletedTask; - } - - #endregion -} \ No newline at end of file diff --git a/Moonlight.ApiServer/Startup/Startup.Auth.cs b/Moonlight.ApiServer/Startup/Startup.Auth.cs new file mode 100644 index 00000000..593ee009 --- /dev/null +++ b/Moonlight.ApiServer/Startup/Startup.Auth.cs @@ -0,0 +1,61 @@ +using System.Text; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.IdentityModel.Tokens; +using MoonCore.Extended.JwtInvalidation; +using MoonCore.Permissions; +using Moonlight.ApiServer.Implementations; +using Moonlight.ApiServer.Interfaces; + +namespace Moonlight.ApiServer.Startup; + +public partial class Startup +{ + private Task RegisterAuth() + { + WebApplicationBuilder.Services + .AddAuthentication("coreAuthentication") + .AddJwtBearer("coreAuthentication", options => + { + options.TokenValidationParameters = new() + { + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes( + Configuration.Authentication.Secret + )), + ValidateIssuerSigningKey = true, + ValidateLifetime = true, + ClockSkew = TimeSpan.Zero, + ValidateAudience = true, + ValidAudience = Configuration.PublicUrl, + ValidateIssuer = true, + ValidIssuer = Configuration.PublicUrl + }; + }); + + WebApplicationBuilder.Services.AddJwtBearerInvalidation("coreAuthentication"); + WebApplicationBuilder.Services.AddScoped(); + + WebApplicationBuilder.Services.AddAuthorization(); + + WebApplicationBuilder.Services.AddAuthorizationPermissions(options => + { + options.ClaimName = "permissions"; + options.Prefix = "permissions:"; + }); + + // Add local oauth2 provider if enabled + if (Configuration.Authentication.EnableLocalOAuth2) + WebApplicationBuilder.Services.AddScoped(); + + return Task.CompletedTask; + } + + private Task UseAuth() + { + WebApplication.UseAuthentication(); + + WebApplication.UseAuthorization(); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Startup/Startup.Base.cs b/Moonlight.ApiServer/Startup/Startup.Base.cs new file mode 100644 index 00000000..6ee388e2 --- /dev/null +++ b/Moonlight.ApiServer/Startup/Startup.Base.cs @@ -0,0 +1,63 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using MoonCore.Extended.Extensions; +using MoonCore.Extensions; +using MoonCore.Helpers; + +namespace Moonlight.ApiServer.Startup; + +public partial class Startup +{ + private Task RegisterBase() + { + WebApplicationBuilder.Services.AutoAddServices(); + WebApplicationBuilder.Services.AddHttpClient(); + + WebApplicationBuilder.Services.AddApiExceptionHandler(); + + // Add pre-existing services + WebApplicationBuilder.Services.AddSingleton(Configuration); + + // Configure controllers + var mvcBuilder = WebApplicationBuilder.Services.AddControllers(); + + // Add plugin assemblies as application parts + foreach (var pluginStartup in PluginStartups.Select(x => x.GetType().Assembly).Distinct()) + mvcBuilder.AddApplicationPart(pluginStartup.GetType().Assembly); + + return Task.CompletedTask; + } + + private Task UseBase() + { + WebApplication.UseRouting(); + WebApplication.UseExceptionHandler(); + + return Task.CompletedTask; + } + + private Task MapBase() + { + WebApplication.MapControllers(); + + if (Configuration.Frontend.EnableHosting) + WebApplication.MapFallbackToController("Index", "Frontend"); + + return Task.CompletedTask; + } + + private Task ConfigureKestrel() + { + WebApplicationBuilder.WebHost.ConfigureKestrel(kestrelOptions => + { + var maxUploadInBytes = ByteConverter + .FromMegaBytes(Configuration.Kestrel.UploadLimit) + .Bytes; + + kestrelOptions.Limits.MaxRequestBodySize = maxUploadInBytes; + }); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Startup/Startup.Config.cs b/Moonlight.ApiServer/Startup/Startup.Config.cs new file mode 100644 index 00000000..1ec38500 --- /dev/null +++ b/Moonlight.ApiServer/Startup/Startup.Config.cs @@ -0,0 +1,35 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using MoonCore.EnvConfiguration; +using MoonCore.Yaml; +using Moonlight.ApiServer.Configuration; + +namespace Moonlight.ApiServer.Startup; + +public partial class Startup +{ + private async Task SetupAppConfiguration() + { + var configPath = Path.Combine("storage", "config.yml"); + + await YamlDefaultGenerator.Generate(configPath); + + // Configure configuration (wow) + var configurationBuilder = new ConfigurationBuilder(); + + configurationBuilder.AddYamlFile(configPath); + configurationBuilder.AddEnvironmentVariables(prefix: "MOONLIGHT_", separator: "_"); + + var configurationRoot = configurationBuilder.Build(); + + // Retrieve configuration + Configuration = AppConfiguration.CreateEmpty(); + configurationRoot.Bind(Configuration); + } + + private Task RegisterAppConfiguration() + { + WebApplicationBuilder.Services.AddSingleton(Configuration); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Startup/Startup.Database.cs b/Moonlight.ApiServer/Startup/Startup.Database.cs new file mode 100644 index 00000000..5f5dcb51 --- /dev/null +++ b/Moonlight.ApiServer/Startup/Startup.Database.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.DependencyInjection; +using MoonCore.Extended.Abstractions; +using MoonCore.Extended.Extensions; + +namespace Moonlight.ApiServer.Startup; + +public partial class Startup +{ + private Task RegisterDatabase() + { + WebApplicationBuilder.Services.AddDatabaseMappings(); + WebApplicationBuilder.Services.AddServiceCollectionAccessor(); + + WebApplicationBuilder.Services.AddScoped(typeof(DatabaseRepository<>)); + + return Task.CompletedTask; + } + + private async Task PrepareDatabase() + { + await WebApplication.Services.EnsureDatabaseMigrated(); + + WebApplication.Services.GenerateDatabaseMappings(); + } +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Startup/Startup.Hangfire.cs b/Moonlight.ApiServer/Startup/Startup.Hangfire.cs new file mode 100644 index 00000000..b06583f6 --- /dev/null +++ b/Moonlight.ApiServer/Startup/Startup.Hangfire.cs @@ -0,0 +1,48 @@ +using Hangfire; +using Hangfire.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Moonlight.ApiServer.Database; + +namespace Moonlight.ApiServer.Startup; + +public partial class Startup +{ + private Task RegisterHangfire() + { + WebApplicationBuilder.Services.AddHangfire((provider, configuration) => + { + configuration.SetDataCompatibilityLevel(CompatibilityLevel.Version_180); + configuration.UseSimpleAssemblyNameTypeSerializer(); + configuration.UseRecommendedSerializerSettings(); + configuration.UseEFCoreStorage(() => + { + var scope = provider.CreateScope(); + return scope.ServiceProvider.GetRequiredService(); + }, new EFCoreStorageOptions()); + }); + + WebApplicationBuilder.Services.AddHangfireServer(); + + WebApplicationBuilder.Logging.AddFilter( + "Hangfire.Server.BackgroundServerProcess", + LogLevel.Warning + ); + + WebApplicationBuilder.Logging.AddFilter( + "Hangfire.BackgroundJobServer", + LogLevel.Warning + ); + + return Task.CompletedTask; + } + + private Task UseHangfire() + { + if (WebApplication.Environment.IsDevelopment()) + WebApplication.UseHangfireDashboard(); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Startup/Startup.Logging.cs b/Moonlight.ApiServer/Startup/Startup.Logging.cs new file mode 100644 index 00000000..49411c03 --- /dev/null +++ b/Moonlight.ApiServer/Startup/Startup.Logging.cs @@ -0,0 +1,63 @@ +using System.Text.Json; +using Microsoft.Extensions.Logging; +using MoonCore.Logging; + +namespace Moonlight.ApiServer.Startup; + +public partial class Startup +{ + private Task SetupLogging() + { + var loggerFactory = new LoggerFactory(); + loggerFactory.AddAnsiConsole(); + + Logger = loggerFactory.CreateLogger(); + + return Task.CompletedTask; + } + + private async Task RegisterLogging() + { + // Configure application logging + WebApplicationBuilder.Logging.ClearProviders(); + WebApplicationBuilder.Logging.AddAnsiConsole(); + WebApplicationBuilder.Logging.AddFile(Path.Combine("storage", "logs", "moonlight.log")); + + // Logging levels + var logConfigPath = Path.Combine("storage", "logConfig.json"); + + // Ensure logging config, add a default one is missing + if (!File.Exists(logConfigPath)) + { + var defaultLogLevels = new Dictionary + { + { "Default", "Information" }, + { "Microsoft.AspNetCore", "Warning" }, + { "System.Net.Http.HttpClient", "Warning" } + }; + + var logLevelsJson = JsonSerializer.Serialize(defaultLogLevels); + await File.WriteAllTextAsync(logConfigPath, logLevelsJson); + } + + // Add logging configuration + var logLevels = JsonSerializer.Deserialize>( + await File.ReadAllTextAsync(logConfigPath) + )!; + + foreach (var level in logLevels) + WebApplicationBuilder.Logging.AddFilter(level.Key, Enum.Parse(level.Value)); + + // Mute exception handler middleware + // https://github.com/dotnet/aspnetcore/issues/19740 + WebApplicationBuilder.Logging.AddFilter( + "Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware", + LogLevel.Critical + ); + + WebApplicationBuilder.Logging.AddFilter( + "Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware", + LogLevel.Critical + ); + } +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Startup/Startup.Misc.cs b/Moonlight.ApiServer/Startup/Startup.Misc.cs new file mode 100644 index 00000000..f8392f05 --- /dev/null +++ b/Moonlight.ApiServer/Startup/Startup.Misc.cs @@ -0,0 +1,73 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Cors.Infrastructure; +using Microsoft.Extensions.DependencyInjection; + +namespace Moonlight.ApiServer.Startup; + +public partial class Startup +{ + private Task PrintVersion() + { + // Fancy start console output... yes very fancy :> + var rainbow = new Crayon.Rainbow(0.5); + foreach (var c in "Moonlight") + { + Console.Write( + rainbow + .Next() + .Bold() + .Text(c.ToString()) + ); + } + + Console.WriteLine(); + + return Task.CompletedTask; + } + + private Task CreateStorage() + { + Directory.CreateDirectory("storage"); + Directory.CreateDirectory(Path.Combine("storage", "logs")); + + return Task.CompletedTask; + } + + private Task RegisterCors() + { + var allowedOrigins = Configuration.Kestrel.AllowedOrigins; + + WebApplicationBuilder.Services.AddCors(options => + { + var cors = new CorsPolicyBuilder(); + + if (allowedOrigins.Contains("*")) + { + cors.SetIsOriginAllowed(_ => true) + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials(); + } + else + { + cors.WithOrigins(allowedOrigins) + .AllowAnyHeader() + .AllowAnyMethod() + .AllowCredentials(); + } + + options.AddDefaultPolicy( + cors.Build() + ); + }); + + return Task.CompletedTask; + } + + private Task UseCors() + { + WebApplication.UseCors(); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Startup/Startup.Plugins.cs b/Moonlight.ApiServer/Startup/Startup.Plugins.cs new file mode 100644 index 00000000..ec01272c --- /dev/null +++ b/Moonlight.ApiServer/Startup/Startup.Plugins.cs @@ -0,0 +1,87 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using MoonCore.Logging; +using Moonlight.ApiServer.Plugins; + +namespace Moonlight.ApiServer.Startup; + +public partial class Startup +{ + private IServiceProvider PluginLoadServiceProvider; + private IPluginStartup[] PluginStartups; + + private Task InitializePlugins() + { + // Create service provider for starting up + var serviceCollection = new ServiceCollection(); + + serviceCollection.AddSingleton(Configuration); + + serviceCollection.AddLogging(builder => + { + builder.ClearProviders(); + builder.AddAnsiConsole(); + }); + + PluginLoadServiceProvider = serviceCollection.BuildServiceProvider(); + + return Task.CompletedTask; + } + + private async Task HookPluginBuild() + { + foreach (var pluginAppStartup in PluginStartups) + { + try + { + await pluginAppStartup.BuildApplication(PluginLoadServiceProvider, WebApplicationBuilder); + } + catch (Exception e) + { + Logger.LogError( + "An error occured while processing 'BuildApp' for '{name}': {e}", + pluginAppStartup.GetType().FullName, + e + ); + } + } + } + + private async Task HookPluginConfigure() + { + foreach (var pluginAppStartup in PluginStartups) + { + try + { + await pluginAppStartup.ConfigureApplication(PluginLoadServiceProvider, WebApplication); + } + catch (Exception e) + { + Logger.LogError( + "An error occured while processing 'ConfigureApp' for '{name}': {e}", + pluginAppStartup.GetType().FullName, + e + ); + } + } + } + + private async Task HookPluginEndpoints() + { + foreach (var pluginEndpointStartup in PluginStartups) + { + try + { + await pluginEndpointStartup.ConfigureEndpoints(PluginLoadServiceProvider, WebApplication); + } + catch (Exception e) + { + Logger.LogError( + "An error occured while processing 'ConfigureEndpoints' for '{name}': {e}", + pluginEndpointStartup.GetType().FullName, + e + ); + } + } + } +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Startup/Startup.cs b/Moonlight.ApiServer/Startup/Startup.cs new file mode 100644 index 00000000..731ca06b --- /dev/null +++ b/Moonlight.ApiServer/Startup/Startup.cs @@ -0,0 +1,67 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Logging; +using Moonlight.ApiServer.Configuration; +using Moonlight.ApiServer.Plugins; + +namespace Moonlight.ApiServer.Startup; + +public partial class Startup +{ + private string[] Args; + + // Logger + public ILogger Logger { get; private set; } + + // Configuration + public AppConfiguration Configuration { get; private set; } + + // WebApplication Stuff + public WebApplication WebApplication { get; private set; } + public WebApplicationBuilder WebApplicationBuilder { get; private set; } + + public Task Initialize(string[] args, IPluginStartup[]? plugins = null) + { + Args = args; + PluginStartups = plugins ?? []; + + return Task.CompletedTask; + } + + public async Task AddMoonlight(WebApplicationBuilder builder) + { + WebApplicationBuilder = builder; + + await PrintVersion(); + + await CreateStorage(); + await SetupAppConfiguration(); + await SetupLogging(); + await InitializePlugins(); + + await ConfigureKestrel(); + await RegisterAppConfiguration(); + await RegisterLogging(); + await RegisterBase(); + await RegisterDatabase(); + await RegisterAuth(); + await RegisterCors(); + await RegisterHangfire(); + await HookPluginBuild(); + } + + public async Task AddMoonlight(WebApplication application) + { + WebApplication = application; + + await PrepareDatabase(); + + await UseCors(); + await UseBase(); + await UseAuth(); + await UseHangfire(); + await HookPluginConfigure(); + + await MapBase(); + await HookPluginEndpoints(); + } +} \ No newline at end of file diff --git a/Moonlight.Client.Runtime/Moonlight.Client.Runtime.csproj b/Moonlight.Client.Runtime/Moonlight.Client.Runtime.csproj index 34411a77..627ac2ce 100644 --- a/Moonlight.Client.Runtime/Moonlight.Client.Runtime.csproj +++ b/Moonlight.Client.Runtime/Moonlight.Client.Runtime.csproj @@ -64,6 +64,7 @@ <_ContentIncludedByDefault Remove="wwwroot\js\moonCore.js" /> <_ContentIncludedByDefault Remove="wwwroot\js\moonlight.js" /> + <_ContentIncludedByDefault Remove="wwwroot\svg\logo.svg" /> diff --git a/Moonlight.Client.Runtime/Program.cs b/Moonlight.Client.Runtime/Program.cs index 5cb70381..16b1f2f4 100644 --- a/Moonlight.Client.Runtime/Program.cs +++ b/Moonlight.Client.Runtime/Program.cs @@ -1,9 +1,20 @@ -using Moonlight.Client; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Moonlight.Client.Runtime; - -var startup = new Startup(); +using Moonlight.Client.Startup; var pluginLoader = new PluginLoader(); pluginLoader.Initialize(); -await startup.Run(args, pluginLoader.Instances); \ No newline at end of file +var startup = new Startup(); + +await startup.Initialize(pluginLoader.Instances); + +var wasmHostBuilder = WebAssemblyHostBuilder.CreateDefault(args); + +await startup.AddMoonlight(wasmHostBuilder); + +var wasmApp = wasmHostBuilder.Build(); + +await startup.AddMoonlight(wasmApp); + +await wasmApp.RunAsync(); \ No newline at end of file diff --git a/Moonlight.Client.Runtime/wwwroot/frontend.example.json b/Moonlight.Client.Runtime/wwwroot/frontend.example.json deleted file mode 100644 index aba7d0b1..00000000 --- a/Moonlight.Client.Runtime/wwwroot/frontend.example.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "apiUrl": "http://localhost:5165", - "hostEnvironment": "Static", - "theme": { - "variables": { - } - }, - "scripts": [ - ], - "plugins": { - "assemblies": [ - ], - "entrypoints": [ - ] - } -} \ No newline at end of file diff --git a/Moonlight.Client.Runtime/wwwroot/img/icon-192.png b/Moonlight.Client.Runtime/wwwroot/img/icon-192.png deleted file mode 100644 index 1b417d98..00000000 Binary files a/Moonlight.Client.Runtime/wwwroot/img/icon-192.png and /dev/null differ diff --git a/Moonlight.Client.Runtime/wwwroot/img/icon-512.png b/Moonlight.Client.Runtime/wwwroot/img/icon-512.png deleted file mode 100644 index 437487cf..00000000 Binary files a/Moonlight.Client.Runtime/wwwroot/img/icon-512.png and /dev/null differ diff --git a/Moonlight.Client.Runtime/wwwroot/img/pfp_placeholder.png b/Moonlight.Client.Runtime/wwwroot/img/pfp_placeholder.png deleted file mode 100644 index 1108ddbb..00000000 Binary files a/Moonlight.Client.Runtime/wwwroot/img/pfp_placeholder.png and /dev/null differ diff --git a/Moonlight.Client/Moonlight.Client.csproj b/Moonlight.Client/Moonlight.Client.csproj index 3a23d5fc..2decf80b 100644 --- a/Moonlight.Client/Moonlight.Client.csproj +++ b/Moonlight.Client/Moonlight.Client.csproj @@ -12,7 +12,7 @@ frontend Moonlight.Client - 2.1.2 + 2.1.3 Moonlight Panel A build of the client for moonlight development https://github.com/Moonlight-Panel/Moonlight @@ -27,16 +27,6 @@ - - true - src - Never - - - true - styles - Never - diff --git a/Moonlight.Client/Startup.cs b/Moonlight.Client/Startup.cs deleted file mode 100644 index 2963ee95..00000000 --- a/Moonlight.Client/Startup.cs +++ /dev/null @@ -1,310 +0,0 @@ -using System.Text.Json; -using Microsoft.AspNetCore.Components.WebAssembly.Hosting; -using Microsoft.Extensions.DependencyInjection; -using MoonCore.Blazor.FlyonUi; -using MoonCore.Blazor.FlyonUi.Auth; -using MoonCore.Blazor.Services; -using MoonCore.Extensions; -using MoonCore.Helpers; -using MoonCore.Logging; -using MoonCore.Permissions; -using Moonlight.Client.Plugins; -using Moonlight.Client.Services; -using Moonlight.Shared.Misc; -using Moonlight.Client.UI; -using WindowService = Moonlight.Client.Services.WindowService; - -namespace Moonlight.Client; - -public class Startup -{ - private string[] Args; - - // Configuration - private FrontendConfiguration Configuration; - - // Logging - private ILoggerFactory LoggerFactory; - private ILogger Logger; - - // WebAssemblyHost - private WebAssemblyHostBuilder WebAssemblyHostBuilder; - private WebAssemblyHost WebAssemblyHost; - - // Plugin Loading - private IPluginStartup[] AdditionalPlugins; - private IPluginStartup[] PluginStartups; - private IServiceProvider PluginLoadServiceProvider; - - public async Task Run(string[] args, IPluginStartup[]? additionalPlugins = null) - { - Args = args; - AdditionalPlugins = additionalPlugins ?? []; - - await PrintVersion(); - await SetupLogging(); - - await CreateWebAssemblyHostBuilder(); - - await LoadConfiguration(); - await InitializePlugins(); - - await RegisterLogging(); - await RegisterBase(); - await RegisterAuthentication(); - await HookPluginBuild(); - - await BuildWebAssemblyHost(); - - await HookPluginConfigure(); - await LoadAssets(); - - await WebAssemblyHost.RunAsync(); - } - - private Task PrintVersion() - { - // Fancy start console output... yes very fancy :> - Console.Write("Running "); - - var rainbow = new Crayon.Rainbow(0.5); - foreach (var c in "Moonlight") - { - Console.Write( - rainbow - .Next() - .Bold() - .Text(c.ToString()) - ); - } - - Console.WriteLine(); - - return Task.CompletedTask; - } - - private async Task LoadConfiguration() - { - try - { - using var httpClient = new HttpClient(); - httpClient.BaseAddress = new Uri(WebAssemblyHostBuilder.HostEnvironment.BaseAddress); - - var jsonText = await httpClient.GetStringAsync("frontend.json"); - - Configuration = JsonSerializer.Deserialize(jsonText, new JsonSerializerOptions() - { - PropertyNameCaseInsensitive = true - })!; - - WebAssemblyHostBuilder.Services.AddSingleton(Configuration); - } - catch (Exception e) - { - Logger.LogCritical("Unable to load configuration. Unable to continue: {e}", e); - throw; - } - } - - private Task RegisterBase() - { - WebAssemblyHostBuilder.RootComponents.Add("#app"); - WebAssemblyHostBuilder.RootComponents.Add("head::after"); - - WebAssemblyHostBuilder.Services.AddScoped(_ => - new HttpClient - { - BaseAddress = new Uri(Configuration.ApiUrl) - } - ); - - WebAssemblyHostBuilder.Services.AddScoped(sp => - { - var httpClient = sp.GetRequiredService(); - var httpApiClient = new HttpApiClient(httpClient); - - var localStorageService = sp.GetRequiredService(); - - httpApiClient.OnConfigureRequest += async request => - { - var accessToken = await localStorageService.GetString("AccessToken"); - - if (string.IsNullOrEmpty(accessToken)) - return; - - request.Headers.Add("Authorization", $"Bearer {accessToken}"); - }; - - return httpApiClient; - }); - - WebAssemblyHostBuilder.Services.AddScoped(); - WebAssemblyHostBuilder.Services.AddFileManagerOperations(); - WebAssemblyHostBuilder.Services.AddFlyonUiServices(); - WebAssemblyHostBuilder.Services.AddScoped(); - - WebAssemblyHostBuilder.Services.AddScoped(); - - WebAssemblyHostBuilder.Services.AutoAddServices(); - - return Task.CompletedTask; - } - - #region Asset Loading - - private async Task LoadAssets() - { - var jsRuntime = WebAssemblyHost.Services.GetRequiredService(); - - foreach (var scriptName in Configuration.Scripts) - await jsRuntime.InvokeVoidAsync("moonlight.assets.loadJavascript", scriptName); - - foreach (var styleName in Configuration.Styles) - await jsRuntime.InvokeVoidAsync("moonlight.assets.loadStylesheet", styleName); - } - - #endregion - - #region Plugins - - private Task InitializePlugins() - { - // Define minimal service collection - var startupSc = new ServiceCollection(); - - // Create logging proxy - startupSc.AddLogging(builder => - { - builder.ClearProviders(); - builder.AddAnsiConsole(); - }); - - PluginLoadServiceProvider = startupSc.BuildServiceProvider(); - - // Collect startups - var pluginStartups = new List(); - - pluginStartups.AddRange(AdditionalPlugins); // Used by the development server - - // Do NOT remove the following comment, as its used to place the plugin startup register calls - // MLBUILD_PLUGIN_STARTUP_HERE - - - PluginStartups = pluginStartups.ToArray(); - - // Add application assembly service - var appAssemblyService = new ApplicationAssemblyService(); - - appAssemblyService.Assemblies.AddRange( - PluginStartups - .Select(x => x.GetType().Assembly) - .Distinct() - ); - - WebAssemblyHostBuilder.Services.AddSingleton(appAssemblyService); - - return Task.CompletedTask; - } - - #region Hooks - - private async Task HookPluginBuild() - { - foreach (var pluginAppStartup in PluginStartups) - { - try - { - await pluginAppStartup.BuildApplication(PluginLoadServiceProvider, WebAssemblyHostBuilder); - } - catch (Exception e) - { - Logger.LogError( - "An error occured while processing 'BuildApp' for '{name}': {e}", - pluginAppStartup.GetType().FullName, - e - ); - } - } - } - - private async Task HookPluginConfigure() - { - foreach (var pluginAppStartup in PluginStartups) - { - try - { - await pluginAppStartup.ConfigureApplication(PluginLoadServiceProvider, WebAssemblyHost); - } - catch (Exception e) - { - Logger.LogError( - "An error occured while processing 'ConfigureApp' for '{name}': {e}", - pluginAppStartup.GetType().FullName, - e - ); - } - } - } - - #endregion - - #endregion - - #region Logging - - private Task SetupLogging() - { - LoggerFactory = new LoggerFactory(); - LoggerFactory.AddAnsiConsole(); - - Logger = LoggerFactory.CreateLogger(); - - return Task.CompletedTask; - } - - private Task RegisterLogging() - { - WebAssemblyHostBuilder.Logging.ClearProviders(); - WebAssemblyHostBuilder.Logging.AddAnsiConsole(); - - return Task.CompletedTask; - } - - #endregion - - #region Web Application - - private Task CreateWebAssemblyHostBuilder() - { - WebAssemblyHostBuilder = WebAssemblyHostBuilder.CreateDefault(Args); - return Task.CompletedTask; - } - - private Task BuildWebAssemblyHost() - { - WebAssemblyHost = WebAssemblyHostBuilder.Build(); - return Task.CompletedTask; - } - - #endregion - - #region Authentication - - private Task RegisterAuthentication() - { - WebAssemblyHostBuilder.Services.AddAuthorizationCore(); - WebAssemblyHostBuilder.Services.AddCascadingAuthenticationState(); - - WebAssemblyHostBuilder.Services.AddAuthenticationStateManager(); - - WebAssemblyHostBuilder.Services.AddAuthorizationPermissions(options => - { - options.ClaimName = "permissions"; - options.Prefix = "permissions:"; - }); - - return Task.CompletedTask; - } - - #endregion -} \ No newline at end of file diff --git a/Moonlight.Client/Startup/Startup.Auth.cs b/Moonlight.Client/Startup/Startup.Auth.cs new file mode 100644 index 00000000..f5677ea1 --- /dev/null +++ b/Moonlight.Client/Startup/Startup.Auth.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.DependencyInjection; +using MoonCore.Blazor.FlyonUi.Auth; +using MoonCore.Permissions; +using Moonlight.Client.Services; + +namespace Moonlight.Client.Startup; + +public partial class Startup +{ + private Task RegisterAuthentication() + { + WebAssemblyHostBuilder.Services.AddAuthorizationCore(); + WebAssemblyHostBuilder.Services.AddCascadingAuthenticationState(); + + WebAssemblyHostBuilder.Services.AddAuthenticationStateManager(); + + WebAssemblyHostBuilder.Services.AddAuthorizationPermissions(options => + { + options.ClaimName = "permissions"; + options.Prefix = "permissions:"; + }); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Moonlight.Client/Startup/Startup.Base.cs b/Moonlight.Client/Startup/Startup.Base.cs new file mode 100644 index 00000000..447c64b4 --- /dev/null +++ b/Moonlight.Client/Startup/Startup.Base.cs @@ -0,0 +1,56 @@ +using Microsoft.Extensions.DependencyInjection; +using MoonCore.Blazor.FlyonUi; +using MoonCore.Blazor.Services; +using MoonCore.Extensions; +using MoonCore.Helpers; +using Moonlight.Client.Services; +using Moonlight.Client.UI; + +namespace Moonlight.Client.Startup; + +public partial class Startup +{ + private Task RegisterBase() + { + WebAssemblyHostBuilder.RootComponents.Add("#app"); + WebAssemblyHostBuilder.RootComponents.Add("head::after"); + + WebAssemblyHostBuilder.Services.AddScoped(_ => + new HttpClient + { + BaseAddress = new Uri(Configuration.ApiUrl) + } + ); + + WebAssemblyHostBuilder.Services.AddScoped(sp => + { + var httpClient = sp.GetRequiredService(); + var httpApiClient = new HttpApiClient(httpClient); + + var localStorageService = sp.GetRequiredService(); + + httpApiClient.OnConfigureRequest += async request => + { + var accessToken = await localStorageService.GetString("AccessToken"); + + if (string.IsNullOrEmpty(accessToken)) + return; + + request.Headers.Add("Authorization", $"Bearer {accessToken}"); + }; + + return httpApiClient; + }); + + WebAssemblyHostBuilder.Services.AddScoped(); + WebAssemblyHostBuilder.Services.AddFileManagerOperations(); + WebAssemblyHostBuilder.Services.AddFlyonUiServices(); + WebAssemblyHostBuilder.Services.AddScoped(); + + WebAssemblyHostBuilder.Services.AddScoped(); + + WebAssemblyHostBuilder.Services.AutoAddServices(); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Moonlight.Client/Startup/Startup.Logging.cs b/Moonlight.Client/Startup/Startup.Logging.cs new file mode 100644 index 00000000..672b0ed5 --- /dev/null +++ b/Moonlight.Client/Startup/Startup.Logging.cs @@ -0,0 +1,24 @@ +using MoonCore.Logging; + +namespace Moonlight.Client.Startup; + +public partial class Startup +{ + private Task SetupLogging() + { + var loggerFactory = new LoggerFactory(); + loggerFactory.AddAnsiConsole(); + + Logger = loggerFactory.CreateLogger(); + + return Task.CompletedTask; + } + + private Task RegisterLogging() + { + WebAssemblyHostBuilder.Logging.ClearProviders(); + WebAssemblyHostBuilder.Logging.AddAnsiConsole(); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Moonlight.Client/Startup/Startup.Misc.cs b/Moonlight.Client/Startup/Startup.Misc.cs new file mode 100644 index 00000000..38b3bd27 --- /dev/null +++ b/Moonlight.Client/Startup/Startup.Misc.cs @@ -0,0 +1,52 @@ +using System.Text.Json; +using Microsoft.Extensions.DependencyInjection; +using Moonlight.Shared.Misc; + +namespace Moonlight.Client.Startup; + +public partial class Startup +{ + private Task PrintVersion() + { + // Fancy start console output... yes very fancy :> + Console.Write("Running "); + + var rainbow = new Crayon.Rainbow(0.5); + foreach (var c in "Moonlight") + { + Console.Write( + rainbow + .Next() + .Bold() + .Text(c.ToString()) + ); + } + + Console.WriteLine(); + + return Task.CompletedTask; + } + + private async Task LoadConfiguration() + { + try + { + using var httpClient = new HttpClient(); + httpClient.BaseAddress = new Uri(WebAssemblyHostBuilder.HostEnvironment.BaseAddress); + + var jsonText = await httpClient.GetStringAsync("frontend.json"); + + Configuration = JsonSerializer.Deserialize(jsonText, new JsonSerializerOptions() + { + PropertyNameCaseInsensitive = true + })!; + + WebAssemblyHostBuilder.Services.AddSingleton(Configuration); + } + catch (Exception e) + { + Logger.LogCritical("Unable to load configuration. Unable to continue: {e}", e); + throw; + } + } +} \ No newline at end of file diff --git a/Moonlight.Client/Startup/Startup.Plugins.cs b/Moonlight.Client/Startup/Startup.Plugins.cs new file mode 100644 index 00000000..2b97e7eb --- /dev/null +++ b/Moonlight.Client/Startup/Startup.Plugins.cs @@ -0,0 +1,78 @@ +using Microsoft.Extensions.DependencyInjection; +using MoonCore.Logging; +using Moonlight.Client.Plugins; +using Moonlight.Client.Services; + +namespace Moonlight.Client.Startup; + +public partial class Startup +{ + private IPluginStartup[] PluginStartups; + private IServiceProvider PluginLoadServiceProvider; + + private Task InitializePlugins() + { + // Define minimal service collection + var startupSc = new ServiceCollection(); + + // Create logging proxy + startupSc.AddLogging(builder => + { + builder.ClearProviders(); + builder.AddAnsiConsole(); + }); + + PluginLoadServiceProvider = startupSc.BuildServiceProvider(); + + // Add application assembly service + var appAssemblyService = new ApplicationAssemblyService(); + + appAssemblyService.Assemblies.AddRange( + PluginStartups + .Select(x => x.GetType().Assembly) + .Distinct() + ); + + WebAssemblyHostBuilder.Services.AddSingleton(appAssemblyService); + + return Task.CompletedTask; + } + + private async Task HookPluginBuild() + { + foreach (var pluginAppStartup in PluginStartups) + { + try + { + await pluginAppStartup.BuildApplication(PluginLoadServiceProvider, WebAssemblyHostBuilder); + } + catch (Exception e) + { + Logger.LogError( + "An error occured while processing 'BuildApp' for '{name}': {e}", + pluginAppStartup.GetType().FullName, + e + ); + } + } + } + + private async Task HookPluginConfigure() + { + foreach (var pluginAppStartup in PluginStartups) + { + try + { + await pluginAppStartup.ConfigureApplication(PluginLoadServiceProvider, WebAssemblyHost); + } + catch (Exception e) + { + Logger.LogError( + "An error occured while processing 'ConfigureApp' for '{name}': {e}", + pluginAppStartup.GetType().FullName, + e + ); + } + } + } +} \ No newline at end of file diff --git a/Moonlight.Client/Startup/Startup.cs b/Moonlight.Client/Startup/Startup.cs new file mode 100644 index 00000000..1d568b6b --- /dev/null +++ b/Moonlight.Client/Startup/Startup.cs @@ -0,0 +1,48 @@ +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Moonlight.Client.Plugins; +using Moonlight.Shared.Misc; + +namespace Moonlight.Client.Startup; + +public partial class Startup +{ + public ILogger Logger { get; private set; } + + // WebAssemblyHost + public WebAssemblyHostBuilder WebAssemblyHostBuilder { get; private set; } + public WebAssemblyHost WebAssemblyHost { get; private set; } + + // Configuration + public FrontendConfiguration Configuration { get; private set; } + + + public Task Initialize(IPluginStartup[]? plugins = null) + { + PluginStartups = plugins ?? []; + + return Task.CompletedTask; + } + + public async Task AddMoonlight(WebAssemblyHostBuilder builder) + { + WebAssemblyHostBuilder = builder; + + await PrintVersion(); + await SetupLogging(); + + await LoadConfiguration(); + await InitializePlugins(); + + await RegisterLogging(); + await RegisterBase(); + await RegisterAuthentication(); + await HookPluginBuild(); + } + + public async Task AddMoonlight(WebAssemblyHost assemblyHost) + { + WebAssemblyHost = assemblyHost; + + await HookPluginConfigure(); + } +} \ No newline at end of file diff --git a/Moonlight.Client/UI/Partials/AppHeader.razor b/Moonlight.Client/UI/Partials/AppHeader.razor index 7cf6266f..883d9879 100644 --- a/Moonlight.Client/UI/Partials/AppHeader.razor +++ b/Moonlight.Client/UI/Partials/AppHeader.razor @@ -23,7 +23,7 @@ class="inline-grid shrink-0 align-middle"> diff --git a/Moonlight.Client/UI/Partials/AppSidebar.razor b/Moonlight.Client/UI/Partials/AppSidebar.razor index 815cf189..428b25a1 100644 --- a/Moonlight.Client/UI/Partials/AppSidebar.razor +++ b/Moonlight.Client/UI/Partials/AppSidebar.razor @@ -24,7 +24,7 @@ Moonlight v2.1 @@ -85,7 +85,7 @@
@@ -118,7 +118,7 @@
+ class="h-8 rounded-full" src="/_content/Moonlight.Client/svg/logo.svg" alt=""/>
Moonlight v2.1
diff --git a/Moonlight.Client/UI/Views/Admin/Api/Create.razor b/Moonlight.Client/UI/Views/Admin/Api/Create.razor index ee5d89fa..ae66d577 100644 --- a/Moonlight.Client/UI/Views/Admin/Api/Create.razor +++ b/Moonlight.Client/UI/Views/Admin/Api/Create.razor @@ -1,6 +1,4 @@ @page "/admin/api/create" - -@using System.Text.Json @using MoonCore.Helpers @using Moonlight.Shared.Http.Requests.Admin.ApiKeys @using Moonlight.Shared.Http.Responses.Admin.ApiKeys diff --git a/Moonlight.Client/UI/Views/Admin/Api/Update.razor b/Moonlight.Client/UI/Views/Admin/Api/Update.razor index 92f1c8e9..27ba1786 100644 --- a/Moonlight.Client/UI/Views/Admin/Api/Update.razor +++ b/Moonlight.Client/UI/Views/Admin/Api/Update.razor @@ -1,6 +1,4 @@ @page "/admin/api/{Id:int}" - -@using System.Text.Json @using MoonCore.Helpers @using Moonlight.Shared.Http.Requests.Admin.ApiKeys @using Moonlight.Shared.Http.Responses.Admin.ApiKeys diff --git a/Moonlight.Client/UI/Views/Admin/Sys/Files.razor b/Moonlight.Client/UI/Views/Admin/Sys/Files.razor index cce5691a..436a6d78 100644 --- a/Moonlight.Client/UI/Views/Admin/Sys/Files.razor +++ b/Moonlight.Client/UI/Views/Admin/Sys/Files.razor @@ -1,7 +1,6 @@ @page "/admin/system/files" @using Microsoft.AspNetCore.Authorization -@using MoonCore.Blazor.Services @using MoonCore.Helpers @using Moonlight.Client.Implementations @using MoonCore.Blazor.FlyonUi.Files.Manager diff --git a/Moonlight.Client/UI/Views/Admin/Users/Create.razor b/Moonlight.Client/UI/Views/Admin/Users/Create.razor index bf741df6..a08c8183 100644 --- a/Moonlight.Client/UI/Views/Admin/Users/Create.razor +++ b/Moonlight.Client/UI/Views/Admin/Users/Create.razor @@ -1,6 +1,4 @@ @page "/admin/users/create" - -@using System.Text.Json @using MoonCore.Helpers @using Moonlight.Shared.Http.Requests.Admin.Users @using MoonCore.Blazor.FlyonUi.Forms diff --git a/Moonlight.Client/UI/Views/Admin/Users/Update.razor b/Moonlight.Client/UI/Views/Admin/Users/Update.razor index eab94872..1cdcfd16 100644 --- a/Moonlight.Client/UI/Views/Admin/Users/Update.razor +++ b/Moonlight.Client/UI/Views/Admin/Users/Update.razor @@ -1,6 +1,4 @@ @page "/admin/users/{Id:int}" - -@using System.Text.Json @using MoonCore.Helpers @using Moonlight.Shared.Http.Requests.Admin.Users @using Moonlight.Shared.Http.Responses.Admin.Users diff --git a/Moonlight.Client.Runtime/wwwroot/svg/logo.svg b/Moonlight.Client/wwwroot/svg/logo.svg similarity index 100% rename from Moonlight.Client.Runtime/wwwroot/svg/logo.svg rename to Moonlight.Client/wwwroot/svg/logo.svg diff --git a/Moonlight.Shared/Moonlight.Shared.csproj b/Moonlight.Shared/Moonlight.Shared.csproj index a3f54aaf..a69f0ff9 100644 --- a/Moonlight.Shared/Moonlight.Shared.csproj +++ b/Moonlight.Shared/Moonlight.Shared.csproj @@ -9,7 +9,7 @@ Moonlight.Shared shared Moonlight.Shared - 2.1.1 + 2.1.3 Moonlight Panel A build of the shared classes for moonlight development https://github.com/Moonlight-Panel/Moonlight diff --git a/Moonlight.sln b/Moonlight.sln index 6ee5c7aa..161e93c8 100644 --- a/Moonlight.sln +++ b/Moonlight.sln @@ -10,6 +10,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moonlight.ApiServer.Runtime EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moonlight.Client.Runtime", "Moonlight.Client.Runtime\Moonlight.Client.Runtime.csproj", "{72F21AA4-4721-4B4C-B2FF-CFDCBB1BCB05}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Runtime", "Runtime", "{DCE3A43F-ACA8-41C6-BE27-3B3AA033B843}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -38,5 +40,7 @@ Global {72F21AA4-4721-4B4C-B2FF-CFDCBB1BCB05}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution + {97FC686D-BC8A-4145-90C7-CA86B598441E} = {DCE3A43F-ACA8-41C6-BE27-3B3AA033B843} + {72F21AA4-4721-4B4C-B2FF-CFDCBB1BCB05} = {DCE3A43F-ACA8-41C6-BE27-3B3AA033B843} EndGlobalSection EndGlobal