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

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<NoDefaultLaunchSettingsFile>True</NoDefaultLaunchSettingsFile>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Moonlight.ApiServer\Moonlight.ApiServer.csproj" />
<ProjectReference Include="..\Moonlight.Client.Runtime\Moonlight.Client.Runtime.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7" />
<PackageReference Include="MoonCore.PluginFramework" Version="1.0.7" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,10 @@
using MoonCore.PluginFramework;
using Moonlight.ApiServer.Plugins;
namespace Moonlight.ApiServer.Runtime;
[PluginLoader]
public partial class PluginLoader : IPluginStartup
{
}

View File

@@ -0,0 +1,9 @@
using Moonlight.ApiServer;
using Moonlight.ApiServer.Runtime;
var startup = new Startup();
var pluginLoader = new PluginLoader();
pluginLoader.Initialize();
await startup.Run(args, pluginLoader.Instances);

View File

@@ -0,0 +1,29 @@
{
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5165",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"HTTP_PROXY": "",
"HTTPS_PROXY": ""
},
"hotReloadEnabled": true
},
"WASM Debug": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5165",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"HTTP_PROXY": "",
"HTTPS_PROXY": ""
},
"hotReloadEnabled": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}"
}
}
}

View File

@@ -16,14 +16,14 @@ namespace Moonlight.ApiServer.Http.Controllers.Admin.Sys;
[Authorize(Policy = "permissions:admin.system.files")] [Authorize(Policy = "permissions:admin.system.files")]
public class FilesController : Controller public class FilesController : Controller
{ {
private readonly string BaseDirectory = PathBuilder.Dir("storage"); private readonly string BaseDirectory = "storage";
private readonly long ChunkSize = ByteConverter.FromMegaBytes(20).Bytes; private readonly long ChunkSize = ByteConverter.FromMegaBytes(20).Bytes;
[HttpGet("list")] [HttpGet("list")]
public Task<FileSystemEntryResponse[]> List([FromQuery] string path) public Task<FileSystemEntryResponse[]> List([FromQuery] string path)
{ {
var safePath = SanitizePath(path); var safePath = SanitizePath(path);
var physicalPath = PathBuilder.Dir(BaseDirectory, safePath); var physicalPath = Path.Combine(BaseDirectory, safePath);
var entries = new List<FileSystemEntryResponse>(); var entries = new List<FileSystemEntryResponse>();
@@ -84,7 +84,7 @@ public class FilesController : Controller
var positionToSkipTo = ChunkSize * chunkId; var positionToSkipTo = ChunkSize * chunkId;
var safePath = SanitizePath(path); var safePath = SanitizePath(path);
var physicalPath = PathBuilder.File(BaseDirectory, safePath); var physicalPath = Path.Combine(BaseDirectory, safePath);
var baseDir = Path.GetDirectoryName(physicalPath); var baseDir = Path.GetDirectoryName(physicalPath);
if (!string.IsNullOrEmpty(baseDir)) if (!string.IsNullOrEmpty(baseDir))
@@ -113,11 +113,11 @@ public class FilesController : Controller
var oldSafePath = SanitizePath(oldPath); var oldSafePath = SanitizePath(oldPath);
var newSafePath = SanitizePath(newPath); var newSafePath = SanitizePath(newPath);
var oldPhysicalDirPath = PathBuilder.Dir(BaseDirectory, oldSafePath); var oldPhysicalDirPath = Path.Combine(BaseDirectory, oldSafePath);
if (Directory.Exists(oldPhysicalDirPath)) if (Directory.Exists(oldPhysicalDirPath))
{ {
var newPhysicalDirPath = PathBuilder.Dir(BaseDirectory, newSafePath); var newPhysicalDirPath = Path.Combine(BaseDirectory, newSafePath);
Directory.Move( Directory.Move(
oldPhysicalDirPath, oldPhysicalDirPath,
@@ -126,8 +126,8 @@ public class FilesController : Controller
} }
else else
{ {
var oldPhysicalFilePath = PathBuilder.File(BaseDirectory, oldSafePath); var oldPhysicalFilePath = Path.Combine(BaseDirectory, oldSafePath);
var newPhysicalFilePath = PathBuilder.File(BaseDirectory, newSafePath); var newPhysicalFilePath = Path.Combine(BaseDirectory, newSafePath);
System.IO.File.Move( System.IO.File.Move(
oldPhysicalFilePath, oldPhysicalFilePath,
@@ -142,13 +142,13 @@ public class FilesController : Controller
public Task Delete([FromQuery] string path) public Task Delete([FromQuery] string path)
{ {
var safePath = SanitizePath(path); var safePath = SanitizePath(path);
var physicalDirPath = PathBuilder.Dir(BaseDirectory, safePath); var physicalDirPath = Path.Combine(BaseDirectory, safePath);
if (Directory.Exists(physicalDirPath)) if (Directory.Exists(physicalDirPath))
Directory.Delete(physicalDirPath, true); Directory.Delete(physicalDirPath, true);
else else
{ {
var physicalFilePath = PathBuilder.File(BaseDirectory, safePath); var physicalFilePath = Path.Combine(BaseDirectory, safePath);
System.IO.File.Delete(physicalFilePath); System.IO.File.Delete(physicalFilePath);
} }
@@ -160,7 +160,7 @@ public class FilesController : Controller
public Task CreateDirectory([FromQuery] string path) public Task CreateDirectory([FromQuery] string path)
{ {
var safePath = SanitizePath(path); var safePath = SanitizePath(path);
var physicalPath = PathBuilder.Dir(BaseDirectory, safePath); var physicalPath = Path.Combine(BaseDirectory, safePath);
Directory.CreateDirectory(physicalPath); Directory.CreateDirectory(physicalPath);
return Task.CompletedTask; return Task.CompletedTask;
@@ -170,7 +170,7 @@ public class FilesController : Controller
public async Task Download([FromQuery] string path) public async Task Download([FromQuery] string path)
{ {
var safePath = SanitizePath(path); var safePath = SanitizePath(path);
var physicalPath = PathBuilder.File(BaseDirectory, safePath); var physicalPath = Path.Combine(BaseDirectory, safePath);
await using var fs = System.IO.File.Open(physicalPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); await using var fs = System.IO.File.Open(physicalPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
await fs.CopyToAsync(Response.Body); await fs.CopyToAsync(Response.Body);
@@ -192,7 +192,7 @@ public class FilesController : Controller
private async Task CompressTarGz(string path, string[] itemsToCompress) private async Task CompressTarGz(string path, string[] itemsToCompress)
{ {
var safePath = SanitizePath(path); var safePath = SanitizePath(path);
var destination = PathBuilder.File(BaseDirectory, safePath); var destination = Path.Combine(BaseDirectory, safePath);
await using var outStream = System.IO.File.Create(destination); await using var outStream = System.IO.File.Create(destination);
await using var gzoStream = new GZipOutputStream(outStream); await using var gzoStream = new GZipOutputStream(outStream);
@@ -201,7 +201,7 @@ public class FilesController : Controller
foreach (var itemName in itemsToCompress) foreach (var itemName in itemsToCompress)
{ {
var safeFilePath = SanitizePath(itemName); var safeFilePath = SanitizePath(itemName);
var filePath = PathBuilder.File(BaseDirectory, safeFilePath); var filePath = Path.Combine(BaseDirectory, safeFilePath);
var fi = new FileInfo(filePath); var fi = new FileInfo(filePath);
@@ -210,7 +210,7 @@ public class FilesController : Controller
else else
{ {
var safeDirePath = SanitizePath(itemName); var safeDirePath = SanitizePath(itemName);
var dirPath = PathBuilder.Dir(BaseDirectory, safeDirePath); var dirPath = Path.Combine(BaseDirectory, safeDirePath);
await AddDirectoryToTarGz(tarStream, dirPath); await AddDirectoryToTarGz(tarStream, dirPath);
} }
@@ -267,7 +267,7 @@ public class FilesController : Controller
private async Task CompressZip(string path, string[] itemsToCompress) private async Task CompressZip(string path, string[] itemsToCompress)
{ {
var safePath = SanitizePath(path); var safePath = SanitizePath(path);
var destination = PathBuilder.File(BaseDirectory, safePath); var destination = Path.Combine(BaseDirectory, safePath);
await using var outStream = System.IO.File.Create(destination); await using var outStream = System.IO.File.Create(destination);
await using var zipOutputStream = new ZipOutputStream(outStream); await using var zipOutputStream = new ZipOutputStream(outStream);
@@ -275,7 +275,7 @@ public class FilesController : Controller
foreach (var itemName in itemsToCompress) foreach (var itemName in itemsToCompress)
{ {
var safeFilePath = SanitizePath(itemName); var safeFilePath = SanitizePath(itemName);
var filePath = PathBuilder.File(BaseDirectory, safeFilePath); var filePath = Path.Combine(BaseDirectory, safeFilePath);
var fi = new FileInfo(filePath); var fi = new FileInfo(filePath);
@@ -284,7 +284,7 @@ public class FilesController : Controller
else else
{ {
var safeDirePath = SanitizePath(itemName); var safeDirePath = SanitizePath(itemName);
var dirPath = PathBuilder.Dir(BaseDirectory, safeDirePath); var dirPath = Path.Combine(BaseDirectory, safeDirePath);
await AddDirectoryToZip(zipOutputStream, dirPath); await AddDirectoryToZip(zipOutputStream, dirPath);
} }
@@ -350,7 +350,7 @@ public class FilesController : Controller
var safeDestination = SanitizePath(destination); var safeDestination = SanitizePath(destination);
var safeArchivePath = SanitizePath(path); var safeArchivePath = SanitizePath(path);
var archivePath = PathBuilder.File(BaseDirectory, safeArchivePath); var archivePath = Path.Combine(BaseDirectory, safeArchivePath);
await using var fs = System.IO.File.Open(archivePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); await using var fs = System.IO.File.Open(archivePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
await using var gzipInputStream = new GZipInputStream(fs); await using var gzipInputStream = new GZipInputStream(fs);
@@ -364,7 +364,7 @@ public class FilesController : Controller
break; break;
var safeFilePath = SanitizePath(entry.Name); var safeFilePath = SanitizePath(entry.Name);
var fileDestination = PathBuilder.File(BaseDirectory, safeDestination, safeFilePath); var fileDestination = Path.Combine(BaseDirectory, safeDestination, safeFilePath);
var parentFolder = Path.GetDirectoryName(fileDestination); var parentFolder = Path.GetDirectoryName(fileDestination);
// Ensure parent directory exists, if it's not the base directory // Ensure parent directory exists, if it's not the base directory
@@ -393,7 +393,7 @@ public class FilesController : Controller
var safeDestination = SanitizePath(destination); var safeDestination = SanitizePath(destination);
var safeArchivePath = SanitizePath(path); var safeArchivePath = SanitizePath(path);
var archivePath = PathBuilder.File(BaseDirectory, safeArchivePath); var archivePath = Path.Combine(BaseDirectory, safeArchivePath);
await using var fs = System.IO.File.Open(archivePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); await using var fs = System.IO.File.Open(archivePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
await using var zipInputStream = new ZipInputStream(fs); await using var zipInputStream = new ZipInputStream(fs);
@@ -409,7 +409,7 @@ public class FilesController : Controller
continue; continue;
var safeFilePath = SanitizePath(entry.Name); var safeFilePath = SanitizePath(entry.Name);
var fileDestination = PathBuilder.File(BaseDirectory, safeDestination, safeFilePath); var fileDestination = Path.Combine(BaseDirectory, safeDestination, safeFilePath);
var parentFolder = Path.GetDirectoryName(fileDestination); var parentFolder = Path.GetDirectoryName(fileDestination);
// Ensure parent directory exists, if it's not the base directory // Ensure parent directory exists, if it's not the base directory

View File

@@ -14,7 +14,7 @@ public class ThemeController : Controller
[Authorize(Policy = "permissions:admin.system.theme.update")] [Authorize(Policy = "permissions:admin.system.theme.update")]
public async Task Patch([FromBody] UpdateThemeRequest request) public async Task Patch([FromBody] UpdateThemeRequest request)
{ {
var themePath = PathBuilder.File("storage", "theme.json"); var themePath = Path.Combine("storage", "theme.json");
await System.IO.File.WriteAllTextAsync( await System.IO.File.WriteAllTextAsync(
themePath, themePath,

View File

@@ -1,14 +1,12 @@
using System.Text.Json; using System.Text;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MoonCore.Exceptions;
using MoonCore.Helpers;
using Moonlight.ApiServer.Configuration;
using Moonlight.ApiServer.Services; using Moonlight.ApiServer.Services;
using Moonlight.Shared.Misc; using Moonlight.Shared.Misc;
namespace Moonlight.ApiServer.Http.Controllers; namespace Moonlight.ApiServer.Http.Controllers.Frontend;
[ApiController] [ApiController]
[Route("/")]
public class FrontendController : Controller public class FrontendController : Controller
{ {
private readonly FrontendService FrontendService; private readonly FrontendService FrontendService;
@@ -21,4 +19,12 @@ public class FrontendController : Controller
[HttpGet("frontend.json")] [HttpGet("frontend.json")]
public async Task<FrontendConfiguration> GetConfiguration() public async Task<FrontendConfiguration> GetConfiguration()
=> await FrontendService.GetConfiguration(); => await FrontendService.GetConfiguration();
[HttpGet]
public async Task<IResult> Index()
{
var content = await FrontendService.GenerateIndexHtml();
return Results.Text(content, "text/html", Encoding.UTF8);
}
} }

View File

@@ -0,0 +1,51 @@
@using Moonlight.Shared.Misc
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@Configuration.Title</title>
<base href="/" />
@foreach (var style in Configuration.Styles)
{
<link rel="stylesheet" href="@style" />
}
<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" />
</head>
<body class="bg-gray-950 text-white font-inter h-full">
<div id="app">
<div class="flex h-screen justify-center items-center">
<div class="sm:max-w-lg">
<div id="blazor-loader-label" class="text-center mb-2 text-lg font-semibold"></div>
<div class="flex flex-col gap-1">
<div class="progress min-w-sm md:min-w-md" role="progressbar">
<div id="blazor-loader-progress" class="progress-bar"></div>
</div>
</div>
</div>
</div>
</div>
@foreach (var script in Configuration.Scripts)
{
<script src="@script"></script>
}
<script src="/_framework/blazor.webassembly.js"></script>
<script>navigator.serviceWorker.register('service-worker.js');</script>
</body>
</html>
@code
{
[Parameter] public FrontendConfiguration Configuration { get; set; }
}

View File

@@ -2,13 +2,11 @@
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MoonCore.Helpers; using MoonCore.Helpers;
using MoonCore.Services;
using Moonlight.ApiServer.Configuration; using Moonlight.ApiServer.Configuration;
using Moonlight.ApiServer.Models; using Moonlight.ApiServer.Models;
namespace Moonlight.ApiServer.Http.Controllers.Swagger; namespace Moonlight.ApiServer.Http.Controllers.Swagger;
[AllowAnonymous]
[Route("api/swagger")] [Route("api/swagger")]
public class SwaggerController : Controller public class SwaggerController : Controller
{ {

View File

@@ -4,6 +4,7 @@ using Moonlight.ApiServer.Database;
using Moonlight.ApiServer.Implementations.Diagnose; using Moonlight.ApiServer.Implementations.Diagnose;
using Moonlight.ApiServer.Implementations.Metrics; using Moonlight.ApiServer.Implementations.Metrics;
using Moonlight.ApiServer.Interfaces; using Moonlight.ApiServer.Interfaces;
using Moonlight.ApiServer.Models;
using Moonlight.ApiServer.Plugins; using Moonlight.ApiServer.Plugins;
using Moonlight.ApiServer.Services; using Moonlight.ApiServer.Services;
using OpenTelemetry.Metrics; using OpenTelemetry.Metrics;
@@ -11,7 +12,6 @@ using OpenTelemetry.Trace;
namespace Moonlight.ApiServer.Implementations.Startup; namespace Moonlight.ApiServer.Implementations.Startup;
[PluginStartup]
public class CoreStartup : IPluginStartup public class CoreStartup : IPluginStartup
{ {
public Task BuildApplication(IServiceProvider serviceProvider, IHostApplicationBuilder builder) public Task BuildApplication(IServiceProvider serviceProvider, IHostApplicationBuilder builder)
@@ -81,6 +81,23 @@ public class CoreStartup : IPluginStartup
#endregion #endregion
#region Client / Frontend
if (configuration.Client.Enable)
{
builder.Services.AddSingleton(new FrontendConfigurationOption()
{
Scripts =
[
"/_content/Moonlight.Client/js/moonlight.js", "/_content/Moonlight.Client/js/moonCore.js",
"/_content/Moonlight.Client/ace/ace.js"
],
Styles = ["/css/style.min.css"]
});
}
#endregion
return Task.CompletedTask; return Task.CompletedTask;
} }

View File

@@ -0,0 +1,62 @@
using System.Security.Claims;
using Microsoft.EntityFrameworkCore;
using MoonCore.Extended.Abstractions;
using MoonCore.Extended.JwtInvalidation;
using Moonlight.ApiServer.Database.Entities;
namespace Moonlight.ApiServer.Implementations;
public class UserAuthInvalidation : IJwtInvalidateHandler
{
private readonly DatabaseRepository<User> UserRepository;
private readonly DatabaseRepository<ApiKey> ApiKeyRepository;
public UserAuthInvalidation(
DatabaseRepository<User> userRepository,
DatabaseRepository<ApiKey> apiKeyRepository
)
{
UserRepository = userRepository;
ApiKeyRepository = apiKeyRepository;
}
public async Task<bool> Handle(ClaimsPrincipal principal)
{
var userIdClaim = principal.FindFirstValue("userId");
if (!string.IsNullOrEmpty(userIdClaim))
{
var userId = int.Parse(userIdClaim);
var user = await UserRepository
.Get()
.FirstOrDefaultAsync(x => x.Id == userId);
if (user == null)
return true; // User is deleted, invalidate session
var iatStr = principal.FindFirstValue("iat")!;
var iat = DateTimeOffset.FromUnixTimeSeconds(long.Parse(iatStr));
// If the token has been issued before the token valid time, its expired, and we want to invalidate it
return user.TokenValidTimestamp > iat;
}
var apiKeyIdClaim = principal.FindFirstValue("apiKeyId");
if (!string.IsNullOrEmpty(apiKeyIdClaim))
{
var apiKeyId = int.Parse(apiKeyIdClaim);
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;
}
return true;
}
}

View File

@@ -6,7 +6,6 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Moonlight.Client\Moonlight.Client.csproj" />
<ProjectReference Include="..\Moonlight.Shared\Moonlight.Shared.csproj" /> <ProjectReference Include="..\Moonlight.Shared\Moonlight.Shared.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -30,13 +29,14 @@
<IsPackable>true</IsPackable> <IsPackable>true</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.18" /> <PackageReference Include="Hangfire.AspNetCore" Version="1.8.20" />
<PackageReference Include="Hangfire.Core" Version="1.8.18" /> <PackageReference Include="Hangfire.Core" Version="1.8.20" />
<PackageReference Include="Hangfire.EntityFrameworkCore" Version="0.7.0" /> <PackageReference Include="Hangfire.EntityFrameworkCore" Version="0.7.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.5" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.5" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7" />
<PackageReference Include="MoonCore" Version="1.8.8" /> <PackageReference Include="MoonCore" Version="1.9.1" />
<PackageReference Include="MoonCore.Extended" Version="1.3.4" /> <PackageReference Include="MoonCore.Extended" Version="1.3.5" />
<PackageReference Include="MoonCore.PluginFramework.Generator" Version="1.0.1" />
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.12.0-beta.1" /> <PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.12.0-beta.1" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0" /> <PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.12.0" /> <PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.12.0" />

View File

@@ -1,7 +0,0 @@
namespace Moonlight.ApiServer.Plugins;
[AttributeUsage(AttributeTargets.Class)]
public class PluginStartupAttribute : Attribute
{
}

View File

@@ -6,6 +6,7 @@ using MoonCore.Attributes;
using MoonCore.Exceptions; using MoonCore.Exceptions;
using MoonCore.Helpers; using MoonCore.Helpers;
using Moonlight.ApiServer.Configuration; using Moonlight.ApiServer.Configuration;
using Moonlight.ApiServer.Http.Controllers.Frontend;
using Moonlight.ApiServer.Models; using Moonlight.ApiServer.Models;
using Moonlight.Shared.Misc; using Moonlight.Shared.Misc;
@@ -17,23 +18,26 @@ public class FrontendService
private readonly AppConfiguration Configuration; private readonly AppConfiguration Configuration;
private readonly IWebHostEnvironment WebHostEnvironment; private readonly IWebHostEnvironment WebHostEnvironment;
private readonly IEnumerable<FrontendConfigurationOption> ConfigurationOptions; private readonly IEnumerable<FrontendConfigurationOption> ConfigurationOptions;
private readonly IServiceProvider ServiceProvider;
public FrontendService( public FrontendService(
AppConfiguration configuration, AppConfiguration configuration,
IWebHostEnvironment webHostEnvironment, IWebHostEnvironment webHostEnvironment,
IEnumerable<FrontendConfigurationOption> configurationOptions IEnumerable<FrontendConfigurationOption> configurationOptions,
IServiceProvider serviceProvider
) )
{ {
Configuration = configuration; Configuration = configuration;
WebHostEnvironment = webHostEnvironment; WebHostEnvironment = webHostEnvironment;
ConfigurationOptions = configurationOptions; ConfigurationOptions = configurationOptions;
ServiceProvider = serviceProvider;
} }
public async Task<FrontendConfiguration> GetConfiguration() public async Task<FrontendConfiguration> GetConfiguration()
{ {
var configuration = new FrontendConfiguration() var configuration = new FrontendConfiguration()
{ {
Title = "Moonlight", Title = "Moonlight", // TODO: CONFIG
ApiUrl = Configuration.PublicUrl, ApiUrl = Configuration.PublicUrl,
HostEnvironment = "ApiServer" HostEnvironment = "ApiServer"
}; };
@@ -62,7 +66,20 @@ public class FrontendService
return configuration; return configuration;
} }
public async Task<Stream> GenerateZip() public async Task<string> GenerateIndexHtml() // TODO: Cache
{
var configuration = await GetConfiguration();
return await ComponentHelper.RenderComponent<FrontendPage>(
ServiceProvider,
parameters =>
{
parameters["Configuration"] = configuration;
}
);
}
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 // We only allow the access to this function when we are actually hosting the frontend
if (!Configuration.Client.Enable) if (!Configuration.Client.Enable)

View File

@@ -12,6 +12,7 @@ using MoonCore.Extended.Helpers;
using MoonCore.Extended.JwtInvalidation; using MoonCore.Extended.JwtInvalidation;
using MoonCore.Extensions; using MoonCore.Extensions;
using MoonCore.Helpers; using MoonCore.Helpers;
using MoonCore.Logging;
using MoonCore.Permissions; using MoonCore.Permissions;
using Moonlight.ApiServer.Configuration; using Moonlight.ApiServer.Configuration;
using Moonlight.ApiServer.Database; using Moonlight.ApiServer.Database;
@@ -111,8 +112,7 @@ public class Startup
private Task CreateStorage() private Task CreateStorage()
{ {
Directory.CreateDirectory("storage"); Directory.CreateDirectory("storage");
Directory.CreateDirectory(PathBuilder.Dir("storage", "logs")); Directory.CreateDirectory(Path.Combine("storage", "logs"));
Directory.CreateDirectory(PathBuilder.Dir("storage", "plugins"));
return Task.CompletedTask; return Task.CompletedTask;
} }
@@ -142,7 +142,7 @@ public class Startup
private Task UseBase() private Task UseBase()
{ {
WebApplication.UseRouting(); WebApplication.UseRouting();
WebApplication.UseApiExceptionHandler(); WebApplication.UseExceptionHandler();
if (Configuration.Client.Enable) if (Configuration.Client.Enable)
{ {
@@ -161,7 +161,7 @@ public class Startup
WebApplication.MapControllers(); WebApplication.MapControllers();
if (Configuration.Client.Enable) if (Configuration.Client.Enable)
WebApplication.MapFallbackToFile("index.html"); WebApplication.MapFallbackToController("Index", "Frontend");
return Task.CompletedTask; return Task.CompletedTask;
} }
@@ -194,7 +194,7 @@ public class Startup
serviceCollection.AddLogging(builder => serviceCollection.AddLogging(builder =>
{ {
builder.ClearProviders(); builder.ClearProviders();
builder.AddProviders(LoggerProviders); builder.AddAnsiConsole();
}); });
PluginLoadServiceProvider = serviceCollection.BuildServiceProvider(); PluginLoadServiceProvider = serviceCollection.BuildServiceProvider();
@@ -202,8 +202,6 @@ public class Startup
// Collect startups // Collect startups
var pluginStartups = new List<IPluginStartup>(); var pluginStartups = new List<IPluginStartup>();
pluginStartups.Add(new CoreStartup());
pluginStartups.AddRange(AdditionalPlugins); // Used by the development server pluginStartups.AddRange(AdditionalPlugins); // Used by the development server
// Do NOT remove the following comment, as its used to place the plugin startup register calls // 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(); var configurationBuilder = new ConfigurationBuilder();
// Ensure configuration file exists // 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)) if (!File.Exists(jsonFilePath))
await File.WriteAllTextAsync(jsonFilePath, JsonSerializer.Serialize(new AppConfiguration())); await File.WriteAllTextAsync(jsonFilePath, JsonSerializer.Serialize(new AppConfiguration()));
@@ -336,18 +334,8 @@ public class Startup
private Task SetupLogging() 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 = new LoggerFactory();
LoggerFactory.AddProviders(LoggerProviders); LoggerFactory.AddAnsiConsole();
Logger = LoggerFactory.CreateLogger<Startup>(); Logger = LoggerFactory.CreateLogger<Startup>();
@@ -358,30 +346,33 @@ public class Startup
{ {
// Configure application logging // Configure application logging
WebApplicationBuilder.Logging.ClearProviders(); WebApplicationBuilder.Logging.ClearProviders();
WebApplicationBuilder.Logging.AddProviders(LoggerProviders); WebApplicationBuilder.Logging.AddAnsiConsole();
WebApplicationBuilder.Logging.AddFile(Path.Combine("storage", "logs", "moonlight.log"));
// Logging levels // 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 // Ensure logging config, add a default one is missing
if (!File.Exists(logConfigPath)) if (!File.Exists(logConfigPath))
{ {
var logLevels = new Dictionary<string, string> var defaultLogLevels = new Dictionary<string, string>
{ {
{ "Default", "Information" }, { "Default", "Information" },
{ "Microsoft.AspNetCore", "Warning" }, { "Microsoft.AspNetCore", "Warning" },
{ "System.Net.Http.HttpClient", "Warning" } { "System.Net.Http.HttpClient", "Warning" }
}; };
var logLevelsJson = JsonSerializer.Serialize(logLevels); var logLevelsJson = JsonSerializer.Serialize(defaultLogLevels);
var logConfig = "{\"LogLevel\":" + logLevelsJson + "}"; await File.WriteAllTextAsync(logConfigPath, logLevelsJson);
await File.WriteAllTextAsync(logConfigPath, logConfig);
} }
// Add logging configuration // Add logging configuration
WebApplicationBuilder.Logging.AddConfiguration( var logLevels = JsonSerializer.Deserialize<Dictionary<string, string>>(
await File.ReadAllTextAsync(logConfigPath) await File.ReadAllTextAsync(logConfigPath)
); )!;
foreach (var level in logLevels)
WebApplicationBuilder.Logging.AddFilter(level.Key, Enum.Parse<LogLevel>(level.Value));
// Mute exception handler middleware // Mute exception handler middleware
// https://github.com/dotnet/aspnetcore/issues/19740 // https://github.com/dotnet/aspnetcore/issues/19740
@@ -406,7 +397,6 @@ public class Startup
WebApplicationBuilder.Services.AddServiceCollectionAccessor(); WebApplicationBuilder.Services.AddServiceCollectionAccessor();
WebApplicationBuilder.Services.AddScoped(typeof(DatabaseRepository<>)); WebApplicationBuilder.Services.AddScoped(typeof(DatabaseRepository<>));
WebApplicationBuilder.Services.AddScoped(typeof(CrudHelper<,>));
return Task.CompletedTask; return Task.CompletedTask;
} }
@@ -443,42 +433,8 @@ public class Startup
}; };
}); });
WebApplicationBuilder.Services.AddJwtInvalidation("coreAuthentication", options => WebApplicationBuilder.Services.AddJwtBearerInvalidation("coreAuthentication");
{ WebApplicationBuilder.Services.AddScoped<IJwtInvalidateHandler, UserAuthInvalidation>();
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.AddAuthorization(); WebApplicationBuilder.Services.AddAuthorization();
@@ -499,8 +455,6 @@ public class Startup
{ {
WebApplication.UseAuthentication(); WebApplication.UseAuthentication();
WebApplication.UseJwtInvalidation();
WebApplication.UseAuthorization(); WebApplication.UseAuthorization();
return Task.CompletedTask; return Task.CompletedTask;

View File

@@ -0,0 +1,64 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<NoDefaultLaunchSettingsFile>True</NoDefaultLaunchSettingsFile>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Moonlight.Client\Moonlight.Client.csproj"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="MoonCore.PluginFramework" Version="1.0.7"/>
<PackageReference Include="MoonCore.PluginFramework.Generator" Version="1.0.1"/>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.5"/>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.5" PrivateAssets="all"/>
</ItemGroup>
<ItemGroup>
<None Update="Styles\exports.css">
<Pack>true</Pack>
<PackagePath>styles</PackagePath>
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
<None Include="Styles\package-lock.json">
<Pack>true</Pack>
<PackagePath>styles</PackagePath>
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
<None Include="Styles\package.json">
<Pack>true</Pack>
<PackagePath>styles</PackagePath>
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
<None Update="Styles\preTailwind.css">
<Pack>true</Pack>
<PackagePath>styles</PackagePath>
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
<None Update="Styles\resolveNuget.js">
<Pack>true</Pack>
<PackagePath>styles</PackagePath>
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
<None Update="Styles\style.css">
<Pack>true</Pack>
<PackagePath>styles</PackagePath>
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="wwwroot\css\" />
</ItemGroup>
<ItemGroup>
<_ContentIncludedByDefault Remove="wwwroot\js\moonCore.js" />
<_ContentIncludedByDefault Remove="wwwroot\js\moonlight.js" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,10 @@
using MoonCore.PluginFramework;
using Moonlight.Client.Plugins;
namespace Moonlight.Client.Runtime;
[PluginLoader]
public partial class PluginLoader : IPluginStartup
{
}

View File

@@ -0,0 +1,9 @@
using Moonlight.Client;
using Moonlight.Client.Runtime;
var startup = new Startup();
var pluginLoader = new PluginLoader();
pluginLoader.Initialize();
await startup.Run(args, pluginLoader.Instances);

View File

@@ -0,0 +1,14 @@
{
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "http://localhost:5165",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -1,6 +1,6 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=fallback') layer; @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=fallback') layer(base);
@import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&display=swap') layer; @import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&display=swap') layer(base);
@import url("https://cdn.jsdelivr.net/npm/lucide-static/font/lucide.css") layer; @import url("https://cdn.jsdelivr.net/npm/lucide-static/font/lucide.css") layer(base);
@theme { @theme {
--font-inter: "Inter", var(--font-sans); --font-inter: "Inter", var(--font-sans);

View File

@@ -6,9 +6,9 @@
"xml2js": "^0.6.2" "xml2js": "^0.6.2"
}, },
"scripts": { "scripts": {
"pretailwind-build": "node resolveNuget.js ../Moonlight.Client.csproj", "pretailwind-build": "node resolveNuget.js ../Moonlight.Client.Runtime.csproj",
"tailwind-build": "npx tailwindcss -i style.css -o ../wwwroot/css/style.min.css", "tailwind-build": "npx tailwindcss -i style.css -o ../wwwroot/css/style.min.css",
"pretailwind": "node resolveNuget.js ../Moonlight.Client.csproj", "pretailwind": "node resolveNuget.js ../Moonlight.Client.Runtime.csproj",
"tailwind": "npx tailwindcss -i style.css -o ../wwwroot/css/style.min.css --watch" "tailwind": "npx tailwindcss -i style.css -o ../wwwroot/css/style.min.css --watch"
} }
} }

View File

@@ -13,4 +13,9 @@
@source "../**/*.razor"; @source "../**/*.razor";
@source "../**/*.cs"; @source "../**/*.cs";
@source "../**/*.html"; @source "../**/*.html";
@source "../../Moonlight.Client/**/*.razor";
@source "../../Moonlight.Client/**/*.cs";
@source "../../Moonlight.Client/**/*.html";
@source "./mappings/*.map"; @source "./mappings/*.map";

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,6 @@
// Global using directives
global using Microsoft.AspNetCore.Components.Web;
global using Microsoft.JSInterop;
global using Microsoft.Extensions.Logging;
global using MoonCore.Blazor.FlyonUi.Components;

View File

@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Moonlight.Client.Interfaces; using Moonlight.Client.Interfaces;
using Moonlight.Client.Plugins; using Moonlight.Client.Plugins;

View File

@@ -1,7 +1,6 @@
using MoonCore.Blazor.Services; using MoonCore.Blazor.Services;
using MoonCore.Blazor.Tailwind.Fm; using MoonCore.Blazor.Tailwind.Fm;
using MoonCore.Blazor.Tailwind.Fm.Models; using MoonCore.Blazor.Tailwind.Fm.Models;
using MoonCore.Blazor.Tailwind.Services;
using MoonCore.Helpers; using MoonCore.Helpers;
using Moonlight.Shared.Http.Requests.Admin.Sys.Files; using Moonlight.Shared.Http.Requests.Admin.Sys.Files;
using Moonlight.Shared.Http.Responses.Admin.Sys; using Moonlight.Shared.Http.Responses.Admin.Sys;

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly"> <Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
@@ -22,11 +22,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Blazor-ApexCharts" Version="6.0.0" /> <PackageReference Include="Blazor-ApexCharts" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.5" /> <PackageReference Include="MoonCore" Version="1.9.1" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.5" PrivateAssets="all" /> <PackageReference Include="MoonCore.Blazor" Version="1.3.1" />
<PackageReference Include="MoonCore" Version="1.8.8" /> <PackageReference Include="MoonCore.Blazor.FlyonUi" Version="1.0.4" />
<PackageReference Include="MoonCore.Blazor" Version="1.3.0" />
<PackageReference Include="MoonCore.Blazor.Tailwind" Version="1.4.7" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="**\*.cs" Exclude="storage\**\*;bin\**\*;obj\**\*"> <None Include="**\*.cs" Exclude="storage\**\*;bin\**\*;obj\**\*">
@@ -56,4 +54,10 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Moonlight.Shared\Moonlight.Shared.csproj" /> <ProjectReference Include="..\Moonlight.Shared\Moonlight.Shared.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Styles\" />
</ItemGroup>
<ItemGroup>
<_ContentIncludedByDefault Remove="wwwroot\css\style.min.css" />
</ItemGroup>
</Project> </Project>

View File

@@ -1,7 +0,0 @@
namespace Moonlight.Client.Plugins;
[AttributeUsage(AttributeTargets.Class)]
public class PluginStartupAttribute : Attribute
{
}

View File

@@ -1,13 +0,0 @@
using Moonlight.Client;
var startup = new Startup();
try
{
await startup.Run(args);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}

View File

@@ -2,8 +2,8 @@
using System.Web; using System.Web;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Authorization;
using MoonCore.Blazor.FlyonUi.Auth;
using MoonCore.Blazor.Services; using MoonCore.Blazor.Services;
using MoonCore.Blazor.Tailwind.Auth;
using MoonCore.Exceptions; using MoonCore.Exceptions;
using MoonCore.Helpers; using MoonCore.Helpers;
using Moonlight.Shared.Http.Requests.Auth; using Moonlight.Shared.Http.Requests.Auth;

View File

@@ -1,5 +1,3 @@
using Microsoft.JSInterop;
namespace Moonlight.Client.Services; namespace Moonlight.Client.Services;
public class WindowService public class WindowService

View File

@@ -1,21 +1,18 @@
using System.Reflection;
using System.Runtime.Loader;
using System.Text.Json; using System.Text.Json;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.JSInterop; using Microsoft.Extensions.DependencyInjection;
using MoonCore.Blazor.FlyonUi;
using MoonCore.Blazor.FlyonUi.Auth;
using MoonCore.Blazor.Services; using MoonCore.Blazor.Services;
using MoonCore.Blazor.Tailwind.Extensions;
using MoonCore.Blazor.Tailwind.Auth;
using MoonCore.Extensions; using MoonCore.Extensions;
using MoonCore.Helpers; using MoonCore.Helpers;
using MoonCore.Logging;
using MoonCore.Permissions; using MoonCore.Permissions;
using Moonlight.Client.Implementations;
using Moonlight.Client.Interfaces;
using Moonlight.Client.Plugins; using Moonlight.Client.Plugins;
using Moonlight.Client.Services; using Moonlight.Client.Services;
using Moonlight.Shared.Misc; using Moonlight.Shared.Misc;
using Moonlight.Client.UI; using Moonlight.Client.UI;
using WindowService = Moonlight.Client.Services.WindowService;
namespace Moonlight.Client; namespace Moonlight.Client;
@@ -27,7 +24,6 @@ public class Startup
private FrontendConfiguration Configuration; private FrontendConfiguration Configuration;
// Logging // Logging
private ILoggerProvider[] LoggerProviders;
private ILoggerFactory LoggerFactory; private ILoggerFactory LoggerFactory;
private ILogger<Startup> Logger; private ILogger<Startup> Logger;
@@ -143,12 +139,13 @@ public class Startup
}); });
WebAssemblyHostBuilder.Services.AddScoped<WindowService>(); WebAssemblyHostBuilder.Services.AddScoped<WindowService>();
WebAssemblyHostBuilder.Services.AddMoonCoreBlazorTailwind(); WebAssemblyHostBuilder.Services.AddFileManagerOperations();
WebAssemblyHostBuilder.Services.AddFlyonUiServices();
WebAssemblyHostBuilder.Services.AddScoped<LocalStorageService>(); WebAssemblyHostBuilder.Services.AddScoped<LocalStorageService>();
WebAssemblyHostBuilder.Services.AddScoped<ThemeService>(); WebAssemblyHostBuilder.Services.AddScoped<ThemeService>();
WebAssemblyHostBuilder.Services.AutoAddServices<Program>(); WebAssemblyHostBuilder.Services.AutoAddServices<Startup>();
return Task.CompletedTask; return Task.CompletedTask;
} }
@@ -179,7 +176,7 @@ public class Startup
startupSc.AddLogging(builder => startupSc.AddLogging(builder =>
{ {
builder.ClearProviders(); builder.ClearProviders();
builder.AddProviders(LoggerProviders); builder.AddAnsiConsole();
}); });
PluginLoadServiceProvider = startupSc.BuildServiceProvider(); PluginLoadServiceProvider = startupSc.BuildServiceProvider();
@@ -187,8 +184,6 @@ public class Startup
// Collect startups // Collect startups
var pluginStartups = new List<IPluginStartup>(); var pluginStartups = new List<IPluginStartup>();
pluginStartups.Add(new CoreStartup());
pluginStartups.AddRange(AdditionalPlugins); // Used by the development server pluginStartups.AddRange(AdditionalPlugins); // Used by the development server
// Do NOT remove the following comment, as its used to place the plugin startup register calls // Do NOT remove the following comment, as its used to place the plugin startup register calls
@@ -259,15 +254,8 @@ public class Startup
private Task SetupLogging() private Task SetupLogging()
{ {
LoggerProviders = LoggerBuildHelper.BuildFromConfiguration(configuration =>
{
configuration.Console.Enable = true;
configuration.Console.EnableAnsiMode = true;
configuration.FileLogging.Enable = false;
});
LoggerFactory = new LoggerFactory(); LoggerFactory = new LoggerFactory();
LoggerFactory.AddProviders(LoggerProviders); LoggerFactory.AddAnsiConsole();
Logger = LoggerFactory.CreateLogger<Startup>(); Logger = LoggerFactory.CreateLogger<Startup>();
@@ -277,7 +265,7 @@ public class Startup
private Task RegisterLogging() private Task RegisterLogging()
{ {
WebAssemblyHostBuilder.Logging.ClearProviders(); WebAssemblyHostBuilder.Logging.ClearProviders();
WebAssemblyHostBuilder.Logging.AddProviders(LoggerProviders); WebAssemblyHostBuilder.Logging.AddAnsiConsole();
return Task.CompletedTask; return Task.CompletedTask;
} }

View File

@@ -22,7 +22,7 @@
<ModalLauncher/> <ModalLauncher/>
<div id="blazor-error-ui" class="fixed bottom-0 left-0 w-full z-50"> <div id="blazor-error-ui" class="fixed bottom-0 left-0 w-full z-50">
<div class="bg-danger-600 text-white p-4 flex flex-row justify-between items-center"> <div class="bg-error text-white p-4 flex flex-row justify-between items-center">
<div class="flex items-center"> <div class="flex items-center">
<i class="icon-bomb text-lg text-white me-2"></i> <i class="icon-bomb text-lg text-white me-2"></i>
<span>An unhandled error has occurred.</span> <span>An unhandled error has occurred.</span>

View File

@@ -1,12 +1,11 @@
@using System.Security.Claims @using System.Security.Claims
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization @using Microsoft.AspNetCore.Components.Authorization
@using MoonCore.Blazor.Tailwind.Auth @using MoonCore.Blazor.FlyonUi.Auth
@using Moonlight.Client.Interfaces @using Moonlight.Client.Interfaces
@using Moonlight.Client.Models @using Moonlight.Client.Models
@using Moonlight.Client.UI.Layouts @using Moonlight.Client.UI.Layouts
@inject ToastService ToastService
@inject NavigationManager Navigation @inject NavigationManager Navigation
@inject AuthenticationStateManager AuthStateManager @inject AuthenticationStateManager AuthStateManager
@inject IEnumerable<ISidebarItemProvider> SidebarItemProviders @inject IEnumerable<ISidebarItemProvider> SidebarItemProviders

View File

@@ -9,7 +9,8 @@
@inject FrontendConfiguration FrontendConfiguration @inject FrontendConfiguration FrontendConfiguration
@inject ThemeService ThemeService @inject ThemeService ThemeService
@inject ToastService ToastService @inject ToastService ToastService
@inject DownloadService DownloadService
@* @inject DownloadService DownloadService *@
<div class="card card-body p-2"> <div class="card card-body p-2">
<div class="flex flex-row items-center justify-end gap-x-2"> <div class="flex flex-row items-center justify-end gap-x-2">
@@ -108,7 +109,7 @@
AddSetting("danger", "danger-300", 252, 165, 165); AddSetting("danger", "danger-300", 252, 165, 165);
AddSetting("danger", "danger-400", 248, 113, 113); AddSetting("danger", "danger-400", 248, 113, 113);
AddSetting("danger", "danger", 239, 68, 68); AddSetting("danger", "danger", 239, 68, 68);
AddSetting("danger", "danger-600", 220, 38, 38); AddSetting("danger", "error", 220, 38, 38);
AddSetting("danger", "danger-700", 185, 28, 28); AddSetting("danger", "danger-700", 185, 28, 28);
AddSetting("danger", "danger-800", 153, 27, 27); AddSetting("danger", "danger-800", 153, 27, 27);
AddSetting("danger", "danger-900", 127, 29, 29); AddSetting("danger", "danger-900", 127, 29, 29);
@@ -192,7 +193,7 @@
{ {
if (FrontendConfiguration.HostEnvironment != "ApiServer") if (FrontendConfiguration.HostEnvironment != "ApiServer")
{ {
await ToastService.Danger( await ToastService.Error(
"Theme Settings", "Theme Settings",
"Unable to save the theme settings. If you are using a static host, you need to configure the colors in the frontend.json file" "Unable to save the theme settings. If you are using a static host, you need to configure the colors in the frontend.json file"
); );
@@ -215,7 +216,7 @@
var json = JsonSerializer.Serialize(ThemeService.Variables); var json = JsonSerializer.Serialize(ThemeService.Variables);
// Download the theme configuration // Download the theme configuration
await DownloadService.DownloadString("theme.json", json); //await DownloadService.DownloadString("theme.json", json);
await ToastService.Success("Successfully exported theme configuration"); await ToastService.Success("Successfully exported theme configuration");
} }
@@ -224,7 +225,7 @@
{ {
if (!eventArgs.File.Name.EndsWith(".json")) if (!eventArgs.File.Name.EndsWith(".json"))
{ {
await ToastService.Danger("Only .json files are allowed"); await ToastService.Error("Only .json files are allowed");
return; return;
} }

View File

@@ -10,7 +10,8 @@
@inject NavigationManager Navigation @inject NavigationManager Navigation
@inject ToastService ToastService @inject ToastService ToastService
@inject AlertService AlertService @inject AlertService AlertService
@inject DownloadService DownloadService
@* @inject DownloadService DownloadService *@
<PageHeader Title="Create API Key"> <PageHeader Title="Create API Key">
<a href="/admin/api" class="btn btn-secondary"> <a href="/admin/api" class="btn btn-secondary">

View File

@@ -2,8 +2,8 @@
@using MoonCore.Helpers @using MoonCore.Helpers
@using MoonCore.Models @using MoonCore.Models
@using MoonCore.Blazor.Tailwind.Dt
@using Moonlight.Shared.Http.Responses.Admin.ApiKeys @using Moonlight.Shared.Http.Responses.Admin.ApiKeys
@using MoonCore.Blazor.FlyonUi.DataTables
@inject HttpApiClient ApiClient @inject HttpApiClient ApiClient
@inject AlertService AlertService @inject AlertService AlertService

View File

@@ -3,7 +3,6 @@
@using System.Text.Json @using System.Text.Json
@using MoonCore.Helpers @using MoonCore.Helpers
@using Moonlight.Shared.Http.Requests.Admin.Users @using Moonlight.Shared.Http.Requests.Admin.Users
@using MoonCore.Blazor.Tailwind.Input2
@inject HttpApiClient ApiClient @inject HttpApiClient ApiClient
@inject NavigationManager Navigation @inject NavigationManager Navigation

View File

@@ -2,8 +2,8 @@
@using MoonCore.Helpers @using MoonCore.Helpers
@using MoonCore.Models @using MoonCore.Models
@using MoonCore.Blazor.Tailwind.Dt
@using Moonlight.Shared.Http.Responses.Admin.Users @using Moonlight.Shared.Http.Responses.Admin.Users
@using MoonCore.Blazor.FlyonUi.DataTables
@inject HttpApiClient ApiClient @inject HttpApiClient ApiClient
@inject AlertService AlertService @inject AlertService AlertService

View File

@@ -4,7 +4,6 @@
@using MoonCore.Helpers @using MoonCore.Helpers
@using Moonlight.Shared.Http.Requests.Admin.Users @using Moonlight.Shared.Http.Requests.Admin.Users
@using Moonlight.Shared.Http.Responses.Admin.Users @using Moonlight.Shared.Http.Responses.Admin.Users
@using MoonCore.Blazor.Tailwind.Input2
@inject HttpApiClient ApiClient @inject HttpApiClient ApiClient
@inject NavigationManager Navigation @inject NavigationManager Navigation

View File

@@ -8,9 +8,7 @@
@using Microsoft.JSInterop @using Microsoft.JSInterop
@using Moonlight.Client @using Moonlight.Client
@using MoonCore.Blazor.Tailwind.Components @using MoonCore.Blazor.FlyonUi.Components
@using MoonCore.Blazor.Tailwind.Alerts @using MoonCore.Blazor.FlyonUi.Modals
@using MoonCore.Blazor.Tailwind.Helpers @using MoonCore.Blazor.FlyonUi.Toasts
@using MoonCore.Blazor.Tailwind.Modals @using MoonCore.Blazor.FlyonUi.Alerts
@using MoonCore.Blazor.Tailwind.Services
@using MoonCore.Blazor.Tailwind.Toasts

View File

@@ -1,38 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Moonlight.Client</title>
<base href="/" />
<link rel="stylesheet" href="/css/style.min.css" />
<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" />
</head>
<body class="bg-gray-950 text-white font-inter h-full">
<div id="app">
<div class="flex h-screen justify-center items-center">
<div class="sm:max-w-lg">
<div id="blazor-loader-label" class="text-center mb-2 text-lg font-semibold"></div>
<div class="flex flex-col gap-1">
<div class="progress min-w-sm md:min-w-md" role="progressbar">
<div id="blazor-loader-progress" class="progress-bar"></div>
</div>
</div>
</div>
</div>
</div>
<script src="/js/moonlight.js"></script>
<script src="/js/moonCore.js"></script>
<script src="/ace/ace.js"></script>
<script src="/_framework/blazor.webassembly.js"></script>
<script>navigator.serviceWorker.register('service-worker.js');</script>
</body>
</html>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -6,6 +6,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moonlight.Client", "Moonlig
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moonlight.Shared", "Moonlight.Shared\Moonlight.Shared.csproj", "{C82E4F2A-91D2-4BC7-9AA7-241FDAAFC823}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moonlight.Shared", "Moonlight.Shared\Moonlight.Shared.csproj", "{C82E4F2A-91D2-4BC7-9AA7-241FDAAFC823}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moonlight.ApiServer.Runtime", "Moonlight.ApiServer.Runtime\Moonlight.ApiServer.Runtime.csproj", "{97FC686D-BC8A-4145-90C7-CA86B598441E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moonlight.Client.Runtime", "Moonlight.Client.Runtime\Moonlight.Client.Runtime.csproj", "{72F21AA4-4721-4B4C-B2FF-CFDCBB1BCB05}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -24,6 +28,14 @@ Global
{C82E4F2A-91D2-4BC7-9AA7-241FDAAFC823}.Debug|Any CPU.Build.0 = Debug|Any CPU {C82E4F2A-91D2-4BC7-9AA7-241FDAAFC823}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C82E4F2A-91D2-4BC7-9AA7-241FDAAFC823}.Release|Any CPU.ActiveCfg = Release|Any CPU {C82E4F2A-91D2-4BC7-9AA7-241FDAAFC823}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C82E4F2A-91D2-4BC7-9AA7-241FDAAFC823}.Release|Any CPU.Build.0 = Release|Any CPU {C82E4F2A-91D2-4BC7-9AA7-241FDAAFC823}.Release|Any CPU.Build.0 = Release|Any CPU
{97FC686D-BC8A-4145-90C7-CA86B598441E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{97FC686D-BC8A-4145-90C7-CA86B598441E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{97FC686D-BC8A-4145-90C7-CA86B598441E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{97FC686D-BC8A-4145-90C7-CA86B598441E}.Release|Any CPU.Build.0 = Release|Any CPU
{72F21AA4-4721-4B4C-B2FF-CFDCBB1BCB05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{72F21AA4-4721-4B4C-B2FF-CFDCBB1BCB05}.Debug|Any CPU.Build.0 = Debug|Any CPU
{72F21AA4-4721-4B4C-B2FF-CFDCBB1BCB05}.Release|Any CPU.ActiveCfg = Release|Any CPU
{72F21AA4-4721-4B4C-B2FF-CFDCBB1BCB05}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
EndGlobalSection EndGlobalSection