Separating runtime from application code to improve building. Upgraded mooncore packages. Started switching to flyonui. Added PluginFramework plugin loading via mooncore

This commit is contained in:
2025-07-11 17:13:37 +02:00
parent 7e158d48c6
commit eaece9e334
67 changed files with 448 additions and 234 deletions

View File

@@ -12,6 +12,7 @@ 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;
@@ -111,8 +112,7 @@ public class Startup
private Task CreateStorage()
{
Directory.CreateDirectory("storage");
Directory.CreateDirectory(PathBuilder.Dir("storage", "logs"));
Directory.CreateDirectory(PathBuilder.Dir("storage", "plugins"));
Directory.CreateDirectory(Path.Combine("storage", "logs"));
return Task.CompletedTask;
}
@@ -142,7 +142,7 @@ public class Startup
private Task UseBase()
{
WebApplication.UseRouting();
WebApplication.UseApiExceptionHandler();
WebApplication.UseExceptionHandler();
if (Configuration.Client.Enable)
{
@@ -161,7 +161,7 @@ public class Startup
WebApplication.MapControllers();
if (Configuration.Client.Enable)
WebApplication.MapFallbackToFile("index.html");
WebApplication.MapFallbackToController("Index", "Frontend");
return Task.CompletedTask;
}
@@ -194,7 +194,7 @@ public class Startup
serviceCollection.AddLogging(builder =>
{
builder.ClearProviders();
builder.AddProviders(LoggerProviders);
builder.AddAnsiConsole();
});
PluginLoadServiceProvider = serviceCollection.BuildServiceProvider();
@@ -202,8 +202,6 @@ public class Startup
// Collect startups
var pluginStartups = new List<IPluginStartup>();
pluginStartups.Add(new CoreStartup());
pluginStartups.AddRange(AdditionalPlugins); // Used by the development server
// Do NOT remove the following comment, as its used to place the plugin startup register calls
@@ -291,7 +289,7 @@ public class Startup
var configurationBuilder = new ConfigurationBuilder();
// Ensure configuration file exists
var jsonFilePath = PathBuilder.File(Directory.GetCurrentDirectory(), "storage", "app.json");
var jsonFilePath = Path.Combine(Directory.GetCurrentDirectory(), "storage", "app.json");
if (!File.Exists(jsonFilePath))
await File.WriteAllTextAsync(jsonFilePath, JsonSerializer.Serialize(new AppConfiguration()));
@@ -336,18 +334,8 @@ public class Startup
private Task SetupLogging()
{
LoggerProviders = LoggerBuildHelper.BuildFromConfiguration(configuration =>
{
configuration.Console.Enable = true;
configuration.Console.EnableAnsiMode = true;
configuration.FileLogging.Enable = true;
configuration.FileLogging.Path = PathBuilder.File("storage", "logs", "latest.log");
configuration.FileLogging.EnableLogRotation = true;
configuration.FileLogging.RotateLogNameTemplate = PathBuilder.File("storage", "logs", "apiserver.{0}.log");
});
LoggerFactory = new LoggerFactory();
LoggerFactory.AddProviders(LoggerProviders);
LoggerFactory.AddAnsiConsole();
Logger = LoggerFactory.CreateLogger<Startup>();
@@ -358,30 +346,33 @@ public class Startup
{
// Configure application logging
WebApplicationBuilder.Logging.ClearProviders();
WebApplicationBuilder.Logging.AddProviders(LoggerProviders);
WebApplicationBuilder.Logging.AddAnsiConsole();
WebApplicationBuilder.Logging.AddFile(Path.Combine("storage", "logs", "moonlight.log"));
// Logging levels
var logConfigPath = PathBuilder.File("storage", "logConfig.json");
var logConfigPath = Path.Combine("storage", "logConfig.json");
// Ensure logging config, add a default one is missing
if (!File.Exists(logConfigPath))
{
var logLevels = new Dictionary<string, string>
var defaultLogLevels = new Dictionary<string, string>
{
{ "Default", "Information" },
{ "Microsoft.AspNetCore", "Warning" },
{ "System.Net.Http.HttpClient", "Warning" }
};
var logLevelsJson = JsonSerializer.Serialize(logLevels);
var logConfig = "{\"LogLevel\":" + logLevelsJson + "}";
await File.WriteAllTextAsync(logConfigPath, logConfig);
var logLevelsJson = JsonSerializer.Serialize(defaultLogLevels);
await File.WriteAllTextAsync(logConfigPath, logLevelsJson);
}
// Add logging configuration
WebApplicationBuilder.Logging.AddConfiguration(
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
@@ -406,7 +397,6 @@ public class Startup
WebApplicationBuilder.Services.AddServiceCollectionAccessor();
WebApplicationBuilder.Services.AddScoped(typeof(DatabaseRepository<>));
WebApplicationBuilder.Services.AddScoped(typeof(CrudHelper<,>));
return Task.CompletedTask;
}
@@ -442,43 +432,9 @@ public class Startup
ValidIssuer = Configuration.PublicUrl
};
});
WebApplicationBuilder.Services.AddJwtInvalidation("coreAuthentication", options =>
{
options.InvalidateTimeProvider = async (provider, principal) =>
{
var userIdClaim = principal.Claims.FirstOrDefault(x => x.Type == "userId");
if (userIdClaim != null)
{
var userId = int.Parse(userIdClaim.Value);
var userRepository = provider.GetRequiredService<DatabaseRepository<User>>();
var user = await userRepository.Get().FirstOrDefaultAsync(x => x.Id == userId);
if (user == null)
return DateTime.MaxValue;
return user.TokenValidTimestamp;
}
var apiKeyIdClaim = principal.Claims.FirstOrDefault(x => x.Type == "apiKeyId");
if (apiKeyIdClaim != null)
{
var apiKeyId = int.Parse(apiKeyIdClaim.Value);
var apiKeyRepository = provider.GetRequiredService<DatabaseRepository<ApiKey>>();
var apiKey = await apiKeyRepository.Get().FirstOrDefaultAsync(x => x.Id == apiKeyId);
// If the api key exists, we don't want to invalidate the request.
// If it doesn't exist we want to invalidate the request
return apiKey == null ? DateTime.MaxValue : DateTime.MinValue;
}
return DateTime.MaxValue;
};
});
WebApplicationBuilder.Services.AddJwtBearerInvalidation("coreAuthentication");
WebApplicationBuilder.Services.AddScoped<IJwtInvalidateHandler, UserAuthInvalidation>();
WebApplicationBuilder.Services.AddAuthorization();
@@ -499,8 +455,6 @@ public class Startup
{
WebApplication.UseAuthentication();
WebApplication.UseJwtInvalidation();
WebApplication.UseAuthorization();
return Task.CompletedTask;