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:
2025-07-14 21:06:54 +02:00
parent 2b62fc141d
commit 14993b9fe7
48 changed files with 763 additions and 1214 deletions

View File

@@ -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
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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
);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View 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();
}
}