Refactored startup. Removed unused usings. Improved nuget package building. Switched to yaml for configuration. Moved asset files. Set correct context type for oauth2 pages. Updated versions
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
using Moonlight.ApiServer;
|
||||
using Moonlight.ApiServer.Runtime;
|
||||
using Moonlight.ApiServer.Runtime;
|
||||
using Moonlight.ApiServer.Startup;
|
||||
|
||||
var pluginLoader = new PluginLoader();
|
||||
pluginLoader.Initialize();
|
||||
@@ -8,21 +8,28 @@ await startup.Run(args, pluginLoader.Instances);
|
||||
|
||||
*/
|
||||
|
||||
var cs = new CleanStartup();
|
||||
var cs = new Startup();
|
||||
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
await cs.Initialize(args, pluginLoader.Instances);
|
||||
|
||||
await cs.AddMoonlight(builder, args, pluginLoader.Instances);
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
await cs.AddMoonlight(builder);
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
await cs.AddMoonlight(app, args, pluginLoader.Instances);
|
||||
await cs.AddMoonlight(app);
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
app.UseWebAssemblyDebugging();
|
||||
// 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();
|
||||
app.UseBlazorFrameworkFiles();
|
||||
app.UseStaticFiles();
|
||||
}
|
||||
|
||||
|
||||
await app.RunAsync();
|
||||
@@ -1,132 +0,0 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moonlight.ApiServer.Configuration;
|
||||
using Moonlight.ApiServer.Plugins;
|
||||
|
||||
namespace Moonlight.ApiServer;
|
||||
|
||||
public class CleanStartup
|
||||
{
|
||||
// Logger
|
||||
private ILogger Logger;
|
||||
|
||||
// Configuration
|
||||
private AppConfiguration Configuration;
|
||||
|
||||
// WebApplication Stuff
|
||||
private WebApplication WebApplication;
|
||||
private WebApplicationBuilder WebApplicationBuilder;
|
||||
|
||||
public async Task AddMoonlight(
|
||||
WebApplicationBuilder builder,
|
||||
string[] args,
|
||||
IPluginStartup[]? plugins = null
|
||||
)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public async Task AddMoonlight(
|
||||
WebApplication application,
|
||||
string[] args,
|
||||
IPluginStartup[]? plugins = null
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
#region Misc
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Base
|
||||
|
||||
private Task RegisterBase()
|
||||
{
|
||||
WebApplicationBuilder.Services.AutoAddServices<Startup>();
|
||||
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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Moonlight.ApiServer.Database.Entities;
|
||||
namespace Moonlight.ApiServer.Database.Entities;
|
||||
|
||||
public class ApiKey
|
||||
{
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Moonlight.ApiServer.Database.Entities;
|
||||
namespace Moonlight.ApiServer.Database.Entities;
|
||||
|
||||
public class User
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -8,13 +7,11 @@ 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;
|
||||
|
||||
@@ -77,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<string, object>()
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
}
|
||||
|
||||
<link href="manifest.webmanifest" rel="manifest" />
|
||||
<link rel="apple-touch-icon" sizes="512x512" href="/img/icon-512.png" />
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="/img/icon-192.png" />
|
||||
<link rel="apple-touch-icon" sizes="512x512" href="/_content/Moonlight.Client/img/icon-512.png" />
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="/_content/Moonlight.Client/img/icon-192.png" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -12,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;
|
||||
|
||||
@@ -87,7 +86,7 @@ public class CoreStartup : IPluginStartup
|
||||
|
||||
#region Client / Frontend
|
||||
|
||||
if (configuration.Client.Enable)
|
||||
if (configuration.Frontend.EnableHosting)
|
||||
{
|
||||
builder.Services.AddSingleton(new FrontendConfigurationOption()
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<PackageId>Moonlight.ApiServer</PackageId>
|
||||
<Version>2.1.2</Version>
|
||||
<Version>2.1.3</Version>
|
||||
<Authors>Moonlight Panel</Authors>
|
||||
<Description>A build of the api server for moonlight development</Description>
|
||||
<PackageProjectUrl>https://github.com/Moonlight-Panel/Moonlight</PackageProjectUrl>
|
||||
@@ -38,16 +38,6 @@
|
||||
<PackageReference Include="Ben.Demystifier" Version="0.4.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="**\*.cs" Exclude="storage\**\*;bin\**\*;obj\**\*">
|
||||
<Pack>true</Pack>
|
||||
<PackagePath>src</PackagePath>
|
||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="**\*.razor" Exclude="storage\**\*;bin\**\*;obj\**\*">
|
||||
<Pack>true</Pack>
|
||||
<PackagePath>src</PackagePath>
|
||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||
</None>
|
||||
<Compile Remove="storage\**\*" />
|
||||
<Content Remove="storage\**\*" />
|
||||
<None Remove="storage\**\*" />
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -83,7 +83,7 @@ public class FrontendService
|
||||
public async Task<Stream> 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
|
||||
|
||||
@@ -1,552 +0,0 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Hangfire;
|
||||
using Hangfire.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Cors.Infrastructure;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
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 StartupX
|
||||
{
|
||||
private string[] Args;
|
||||
|
||||
// Logging
|
||||
private ILoggerFactory LoggerFactory;
|
||||
private ILogger<Startup> 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<Startup>();
|
||||
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<IPluginStartup>();
|
||||
|
||||
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<AppConfiguration>()!;
|
||||
}
|
||||
|
||||
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<Startup>();
|
||||
|
||||
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<string, string>
|
||||
{
|
||||
{ "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<Dictionary<string, string>>(
|
||||
await File.ReadAllTextAsync(logConfigPath)
|
||||
)!;
|
||||
|
||||
foreach (var level in logLevels)
|
||||
WebApplicationBuilder.Logging.AddFilter(level.Key, Enum.Parse<LogLevel>(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<IJwtInvalidateHandler, UserAuthInvalidation>();
|
||||
|
||||
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<IOAuth2Provider, LocalOAuth2Provider>();
|
||||
|
||||
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<CoreDataContext>();
|
||||
}, 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
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Moonlight.ApiServer.Configuration;
|
||||
using Moonlight.ApiServer.Plugins;
|
||||
|
||||
namespace Moonlight.ApiServer.Startup;
|
||||
|
||||
public partial class CleanStartup
|
||||
{
|
||||
// Logger
|
||||
private ILogger Logger;
|
||||
|
||||
// Configuration
|
||||
private AppConfiguration Configuration;
|
||||
|
||||
// WebApplication Stuff
|
||||
private WebApplication WebApplication;
|
||||
private WebApplicationBuilder WebApplicationBuilder;
|
||||
|
||||
public async Task AddMoonlight(
|
||||
WebApplicationBuilder builder,
|
||||
string[] args,
|
||||
IPluginStartup[]? plugins = null
|
||||
)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public async Task AddMoonlight(
|
||||
WebApplication application,
|
||||
string[] args,
|
||||
IPluginStartup[]? plugins = null
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
#region Misc
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,6 +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 CleanStartup
|
||||
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<IJwtInvalidateHandler, UserAuthInvalidation>();
|
||||
|
||||
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<IOAuth2Provider, LocalOAuth2Provider>();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task UseAuth()
|
||||
{
|
||||
WebApplication.UseAuthentication();
|
||||
|
||||
WebApplication.UseAuthorization();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,17 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using MoonCore.Extended.Extensions;
|
||||
using MoonCore.Extensions;
|
||||
using MoonCore.Helpers;
|
||||
using Moonlight.ApiServer.Plugins;
|
||||
|
||||
namespace Moonlight.ApiServer.Startup;
|
||||
|
||||
public partial class CleanStartup
|
||||
public partial class Startup
|
||||
{
|
||||
private Task RegisterBase(IPluginStartup[] pluginStartups)
|
||||
private Task RegisterBase()
|
||||
{
|
||||
WebApplicationBuilder.Services.AutoAddServices<CleanStartup>();
|
||||
WebApplicationBuilder.Services.AutoAddServices<Startup>();
|
||||
WebApplicationBuilder.Services.AddHttpClient();
|
||||
|
||||
WebApplicationBuilder.Services.AddApiExceptionHandler();
|
||||
@@ -25,7 +23,7 @@ public partial class CleanStartup
|
||||
var mvcBuilder = WebApplicationBuilder.Services.AddControllers();
|
||||
|
||||
// Add plugin assemblies as application parts
|
||||
foreach (var pluginStartup in pluginStartups.Select(x => x.GetType().Assembly).Distinct())
|
||||
foreach (var pluginStartup in PluginStartups.Select(x => x.GetType().Assembly).Distinct())
|
||||
mvcBuilder.AddApplicationPart(pluginStartup.GetType().Assembly);
|
||||
|
||||
return Task.CompletedTask;
|
||||
@@ -36,15 +34,6 @@ public partial class CleanStartup
|
||||
WebApplication.UseRouting();
|
||||
WebApplication.UseExceptionHandler();
|
||||
|
||||
if (Configuration.Client.Enable)
|
||||
{
|
||||
if (WebApplication.Environment.IsDevelopment())
|
||||
WebApplication.UseWebAssemblyDebugging();
|
||||
|
||||
WebApplication.UseBlazorFrameworkFiles();
|
||||
WebApplication.UseStaticFiles();
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -52,7 +41,7 @@ public partial class CleanStartup
|
||||
{
|
||||
WebApplication.MapControllers();
|
||||
|
||||
if (Configuration.Client.Enable)
|
||||
if (Configuration.Frontend.EnableHosting)
|
||||
WebApplication.MapFallbackToController("Index", "Frontend");
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
||||
@@ -1,6 +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 CleanStartup
|
||||
public partial class Startup
|
||||
{
|
||||
|
||||
private async Task SetupAppConfiguration()
|
||||
{
|
||||
var configPath = Path.Combine("storage", "config.yml");
|
||||
|
||||
await YamlDefaultGenerator.Generate<AppConfiguration>(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;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,25 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MoonCore.Extended.Abstractions;
|
||||
using MoonCore.Extended.Extensions;
|
||||
|
||||
namespace Moonlight.ApiServer.Startup;
|
||||
|
||||
public partial class CleanStartup
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +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 CleanStartup
|
||||
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<CoreDataContext>();
|
||||
}, 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;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,63 @@
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MoonCore.Logging;
|
||||
|
||||
namespace Moonlight.ApiServer.Startup;
|
||||
|
||||
public partial class CleanStartup
|
||||
public partial class Startup
|
||||
{
|
||||
|
||||
private Task SetupLogging()
|
||||
{
|
||||
var loggerFactory = new LoggerFactory();
|
||||
loggerFactory.AddAnsiConsole();
|
||||
|
||||
Logger = loggerFactory.CreateLogger<Startup>();
|
||||
|
||||
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<string, string>
|
||||
{
|
||||
{ "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<Dictionary<string, string>>(
|
||||
await File.ReadAllTextAsync(logConfigPath)
|
||||
)!;
|
||||
|
||||
foreach (var level in logLevels)
|
||||
WebApplicationBuilder.Logging.AddFilter(level.Key, Enum.Parse<LogLevel>(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
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,73 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Cors.Infrastructure;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Moonlight.ApiServer.Startup;
|
||||
|
||||
public partial class CleanStartup
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,12 @@ using Moonlight.ApiServer.Plugins;
|
||||
|
||||
namespace Moonlight.ApiServer.Startup;
|
||||
|
||||
public partial class CleanStartup
|
||||
public partial class Startup
|
||||
{
|
||||
private IServiceProvider PluginLoadServiceProvider;
|
||||
private IPluginStartup[] PluginStartups;
|
||||
|
||||
private Task InitializePlugins(IPluginStartup[] pluginStartups)
|
||||
private Task InitializePlugins()
|
||||
{
|
||||
// Create service provider for starting up
|
||||
var serviceCollection = new ServiceCollection();
|
||||
@@ -24,8 +24,6 @@ public partial class CleanStartup
|
||||
});
|
||||
|
||||
PluginLoadServiceProvider = serviceCollection.BuildServiceProvider();
|
||||
|
||||
PluginStartups = pluginStartups;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
67
Moonlight.ApiServer/Startup/Startup.cs
Normal file
67
Moonlight.ApiServer/Startup/Startup.cs
Normal file
@@ -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<Startup> 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();
|
||||
}
|
||||
}
|
||||
@@ -64,6 +64,7 @@
|
||||
<ItemGroup>
|
||||
<_ContentIncludedByDefault Remove="wwwroot\js\moonCore.js" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\js\moonlight.js" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\svg\logo.svg" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -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);
|
||||
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();
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"apiUrl": "http://localhost:5165",
|
||||
"hostEnvironment": "Static",
|
||||
"theme": {
|
||||
"variables": {
|
||||
}
|
||||
},
|
||||
"scripts": [
|
||||
],
|
||||
"plugins": {
|
||||
"assemblies": [
|
||||
],
|
||||
"entrypoints": [
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 8.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 41 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 163 KiB |
@@ -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<Startup> 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<FrontendConfiguration>(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>("#app");
|
||||
WebAssemblyHostBuilder.RootComponents.Add<HeadOutlet>("head::after");
|
||||
|
||||
WebAssemblyHostBuilder.Services.AddScoped(_ =>
|
||||
new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri(Configuration.ApiUrl)
|
||||
}
|
||||
);
|
||||
|
||||
WebAssemblyHostBuilder.Services.AddScoped(sp =>
|
||||
{
|
||||
var httpClient = sp.GetRequiredService<HttpClient>();
|
||||
var httpApiClient = new HttpApiClient(httpClient);
|
||||
|
||||
var localStorageService = sp.GetRequiredService<LocalStorageService>();
|
||||
|
||||
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<WindowService>();
|
||||
WebAssemblyHostBuilder.Services.AddFileManagerOperations();
|
||||
WebAssemblyHostBuilder.Services.AddFlyonUiServices();
|
||||
WebAssemblyHostBuilder.Services.AddScoped<LocalStorageService>();
|
||||
|
||||
WebAssemblyHostBuilder.Services.AddScoped<ThemeService>();
|
||||
|
||||
WebAssemblyHostBuilder.Services.AutoAddServices<Startup>();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
#region Asset Loading
|
||||
|
||||
private async Task LoadAssets()
|
||||
{
|
||||
var jsRuntime = WebAssemblyHost.Services.GetRequiredService<IJSRuntime>();
|
||||
|
||||
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<IPluginStartup>();
|
||||
|
||||
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<Startup>();
|
||||
|
||||
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<RemoteAuthStateManager>();
|
||||
|
||||
WebAssemblyHostBuilder.Services.AddAuthorizationPermissions(options =>
|
||||
{
|
||||
options.ClaimName = "permissions";
|
||||
options.Prefix = "permissions:";
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
25
Moonlight.Client/Startup/Startup.Auth.cs
Normal file
25
Moonlight.Client/Startup/Startup.Auth.cs
Normal file
@@ -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<RemoteAuthStateManager>();
|
||||
|
||||
WebAssemblyHostBuilder.Services.AddAuthorizationPermissions(options =>
|
||||
{
|
||||
options.ClaimName = "permissions";
|
||||
options.Prefix = "permissions:";
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
56
Moonlight.Client/Startup/Startup.Base.cs
Normal file
56
Moonlight.Client/Startup/Startup.Base.cs
Normal file
@@ -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>("#app");
|
||||
WebAssemblyHostBuilder.RootComponents.Add<HeadOutlet>("head::after");
|
||||
|
||||
WebAssemblyHostBuilder.Services.AddScoped(_ =>
|
||||
new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri(Configuration.ApiUrl)
|
||||
}
|
||||
);
|
||||
|
||||
WebAssemblyHostBuilder.Services.AddScoped(sp =>
|
||||
{
|
||||
var httpClient = sp.GetRequiredService<HttpClient>();
|
||||
var httpApiClient = new HttpApiClient(httpClient);
|
||||
|
||||
var localStorageService = sp.GetRequiredService<LocalStorageService>();
|
||||
|
||||
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<WindowService>();
|
||||
WebAssemblyHostBuilder.Services.AddFileManagerOperations();
|
||||
WebAssemblyHostBuilder.Services.AddFlyonUiServices();
|
||||
WebAssemblyHostBuilder.Services.AddScoped<LocalStorageService>();
|
||||
|
||||
WebAssemblyHostBuilder.Services.AddScoped<ThemeService>();
|
||||
|
||||
WebAssemblyHostBuilder.Services.AutoAddServices<Startup>();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
24
Moonlight.Client/Startup/Startup.Logging.cs
Normal file
24
Moonlight.Client/Startup/Startup.Logging.cs
Normal file
@@ -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<Startup>();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task RegisterLogging()
|
||||
{
|
||||
WebAssemblyHostBuilder.Logging.ClearProviders();
|
||||
WebAssemblyHostBuilder.Logging.AddAnsiConsole();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
52
Moonlight.Client/Startup/Startup.Misc.cs
Normal file
52
Moonlight.Client/Startup/Startup.Misc.cs
Normal file
@@ -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<FrontendConfiguration>(jsonText, new JsonSerializerOptions()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
})!;
|
||||
|
||||
WebAssemblyHostBuilder.Services.AddSingleton(Configuration);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogCritical("Unable to load configuration. Unable to continue: {e}", e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
78
Moonlight.Client/Startup/Startup.Plugins.cs
Normal file
78
Moonlight.Client/Startup/Startup.Plugins.cs
Normal file
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
48
Moonlight.Client/Startup/Startup.cs
Normal file
48
Moonlight.Client/Startup/Startup.cs
Normal file
@@ -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<Startup> 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();
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@
|
||||
class="inline-grid shrink-0 align-middle">
|
||||
<img
|
||||
class="h-8 rounded-full"
|
||||
src="/svg/logo.svg"
|
||||
src="/_content/Moonlight.Client/svg/logo.svg"
|
||||
alt=""/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<span
|
||||
class="inline-grid shrink-0 align-middle">
|
||||
<img class="h-8 rounded-full"
|
||||
src="/svg/logo.svg"
|
||||
src="/_content/Moonlight.Client/svg/logo.svg"
|
||||
alt=""/>
|
||||
</span>
|
||||
<span class="truncate">Moonlight v2.1</span>
|
||||
@@ -85,7 +85,7 @@
|
||||
<div class="flex min-w-0 items-center gap-3">
|
||||
<span class="inline-grid shrink-0 align-middle">
|
||||
<img class="h-8 rounded-full"
|
||||
src="/img/pfp_placeholder.png"
|
||||
src="/_content/Moonlight.Client/img/pfp_placeholder.png"
|
||||
alt=""/>
|
||||
</span>
|
||||
<div class="min-w-0">
|
||||
@@ -118,7 +118,7 @@
|
||||
<div data-slot="avatar"
|
||||
class="inline-grid shrink-0 align-middle">
|
||||
<img
|
||||
class="h-8 rounded-full" src="/placeholder.jpg" alt=""/>
|
||||
class="h-8 rounded-full" src="/_content/Moonlight.Client/svg/logo.svg" alt=""/>
|
||||
</div>
|
||||
<div class="truncate">Moonlight v2.1</div>
|
||||
</div>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user