Made moonlight able to compile and somehwat run with MoonCore.Blazor and the new logging system

This commit is contained in:
Marcel Baumgartner
2024-06-22 11:28:58 +02:00
parent 2060b9140f
commit 42e9f18fb6
102 changed files with 909 additions and 798 deletions

View File

@@ -3,14 +3,10 @@ using Microsoft.AspNetCore.Components;
using MoonCore.Abstractions; using MoonCore.Abstractions;
using MoonCore.Helpers; using MoonCore.Helpers;
using MoonCore.Services; using MoonCore.Services;
using MoonCoreUI.Extensions;
using MoonCoreUI.Services;
using Moonlight.Core.Configuration; using Moonlight.Core.Configuration;
using Moonlight.Core.Database; using Moonlight.Core.Database;
using Moonlight.Core.Database.Entities; using Moonlight.Core.Database.Entities;
using Moonlight.Core.Implementations.Diagnose; using Moonlight.Core.Implementations.Diagnose;
using Moonlight.Core.Implementations.UI.Admin.AdminColumns;
using Moonlight.Core.Implementations.UI.Index;
using Moonlight.Core.Interfaces; using Moonlight.Core.Interfaces;
using Moonlight.Core.Interfaces.Ui.Admin; using Moonlight.Core.Interfaces.Ui.Admin;
using Moonlight.Core.Interfaces.UI.User; using Moonlight.Core.Interfaces.UI.User;
@@ -21,10 +17,16 @@ using Moonlight.Core.Models.Enums;
using Moonlight.Core.Repositories; using Moonlight.Core.Repositories;
using Moonlight.Core.Services; using Moonlight.Core.Services;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using MoonCore.Blazor.Extensions;
using MoonCore.Blazor.Services;
using MoonCore.Extensions;
using Moonlight.Core.Attributes; using Moonlight.Core.Attributes;
using Moonlight.Core.Http.Middleware; using Moonlight.Core.Http.Middleware;
using Moonlight.Core.Implementations.AdminDashboard;
using Moonlight.Core.Implementations.ApiDefinition; using Moonlight.Core.Implementations.ApiDefinition;
using Moonlight.Core.Implementations.UserDashboard;
using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.SwaggerGen;
using IdentityService = Moonlight.Core.Services.IdentityService;
namespace Moonlight.Core; namespace Moonlight.Core;
@@ -54,26 +56,22 @@ public class CoreFeature : MoonlightFeature
builder.Services.AddDbContext<DataContext>(); builder.Services.AddDbContext<DataContext>();
// //
builder.Services.AddSingleton(new JwtService<CoreJwtType>(config.Security.Token)); builder.Services.AddSingleton(new JwtService<CoreJwtType>(
config.Security.Token,
context.LoggerFactory.CreateLogger<JwtService<CoreJwtType>>()
)
);
// Mooncore services // Mooncore services
builder.Services.AddScoped(typeof(Repository<>), typeof(GenericRepository<>)); builder.Services.AddScoped(typeof(Repository<>), typeof(GenericRepository<>));
builder.Services.AddScoped<CookieService>();
builder.Services.AddScoped<FileDownloadService>();
builder.Services.AddScoped<AlertService>();
builder.Services.AddScoped<ToastService>();
builder.Services.AddScoped<ClipboardService>();
builder.Services.AddScoped<ModalService>();
builder.Services.AddMoonCoreUi(configuration => builder.Services.AddMoonCore(configuration =>
{ {
configuration.ToastJavascriptPrefix = "moonlight.toasts"; configuration.Identity.Token = config.Security.Token;
configuration.ModalJavascriptPrefix = "moonlight.modals";
configuration.AlertJavascriptPrefix = "moonlight.alerts";
configuration.ClipboardJavascriptPrefix = "moonlight.clipboard";
configuration.FileDownloadJavascriptPrefix = "moonlight.utils";
}); });
builder.Services.AddMoonCoreBlazor();
// Add external services and blazor/asp.net stuff // Add external services and blazor/asp.net stuff
builder.Services.AddRazorPages(); builder.Services.AddRazorPages();
builder.Services.AddHttpContextAccessor(); builder.Services.AddHttpContextAccessor();
@@ -218,9 +216,9 @@ public class CoreFeature : MoonlightFeature
{ {
using var scope = provider.CreateScope(); using var scope = provider.CreateScope();
var configService = scope.ServiceProvider.GetRequiredService<ConfigService<CoreConfiguration>>();
var userRepo = scope.ServiceProvider.GetRequiredService<Repository<User>>(); var userRepo = scope.ServiceProvider.GetRequiredService<Repository<User>>();
var authenticationProvider = scope.ServiceProvider.GetRequiredService<IAuthenticationProvider>(); var authenticationProvider = scope.ServiceProvider.GetRequiredService<IAuthenticationProvider>();
var logger = scope.ServiceProvider.GetRequiredService<ILogger<CoreFeature>>();
if (!configService.Get().Authentication.UseDefaultAuthentication) if (!configService.Get().Authentication.UseDefaultAuthentication)
return; return;
@@ -238,7 +236,7 @@ public class CoreFeature : MoonlightFeature
if (registeredUser == null) if (registeredUser == null)
{ {
Logger.Warn("Unable to create default user. Register function returned null"); logger.LogWarning("Unable to create default user. Register function returned null");
return; return;
} }
@@ -247,7 +245,7 @@ public class CoreFeature : MoonlightFeature
user.Permissions = 9999; user.Permissions = 9999;
userRepo.Update(user); userRepo.Update(user);
Logger.Info($"Default login: Email: '{email}' Password: '{password}'"); logger.LogInformation("Default login: Email: '{email}' Password: '{password}'", email, password);
}); });
// Api // Api

View File

@@ -1,8 +1,15 @@
using MoonCore.Helpers; using MoonCore.Attributes;
using MoonCore.Helpers;
namespace Moonlight.Core.Events; namespace Moonlight.Core.Events;
[Singleton]
public class CoreEvents public class CoreEvents
{ {
public static SmartEventHandler OnMoonlightRestart { get; set; } = new(); public CoreEvents(ILogger<SmartEventHandler> logger)
{
OnMoonlightRestart = new(logger);
}
public SmartEventHandler OnMoonlightRestart { get; set; }
} }

View File

@@ -1,12 +1,21 @@
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using MoonCore.Attributes;
using MoonCore.Helpers; using MoonCore.Helpers;
namespace Moonlight.Core.Helpers; namespace Moonlight.Core.Helpers;
public static class HostSystemHelper [Singleton]
public class HostSystemHelper
{ {
public static Task<string> GetOsName() private readonly ILogger<HostSystemHelper> Logger;
public HostSystemHelper(ILogger<HostSystemHelper> logger)
{
Logger = logger;
}
public Task<string> GetOsName()
{ {
try try
{ {
@@ -48,21 +57,20 @@ public static class HostSystemHelper
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warn("Error retrieving os information"); Logger.LogWarning("Error retrieving os information: {e}", e);
Logger.Warn(e);
return Task.FromResult("N/A"); return Task.FromResult("N/A");
} }
} }
public static Task<long> GetMemoryUsage() public Task<long> GetMemoryUsage()
{ {
var process = Process.GetCurrentProcess(); var process = Process.GetCurrentProcess();
var bytes = process.PrivateMemorySize64; var bytes = process.PrivateMemorySize64;
return Task.FromResult(bytes); return Task.FromResult(bytes);
} }
public static Task<int> GetCpuUsage() public Task<int> GetCpuUsage()
{ {
var process = Process.GetCurrentProcess(); var process = Process.GetCurrentProcess();
var cpuTime = process.TotalProcessorTime; var cpuTime = process.TotalProcessorTime;

View File

@@ -9,13 +9,20 @@ namespace Moonlight.Core.Http.Controllers;
[Route("api/core/asset")] [Route("api/core/asset")]
public class AssetController : Controller public class AssetController : Controller
{ {
private readonly ILogger<AssetController> Logger;
public AssetController(ILogger<AssetController> logger)
{
Logger = logger;
}
[HttpGet("{name}/{*path}")] [HttpGet("{name}/{*path}")]
public async Task<ActionResult> Get(string name, string path) public async Task<ActionResult> Get(string name, string path)
{ {
// Check for path transversal attacks // Check for path transversal attacks
if (path.Contains("..") || name.Contains("..")) if (path.Contains("..") || name.Contains(".."))
{ {
Logger.Warn($"{HttpContext.Connection.RemoteIpAddress} tried to use path transversal attack: {name}/{path}"); Logger.LogWarning("{remoteIp} tried to use path transversal attack: {name}/{path}", HttpContext.Connection.RemoteIpAddress, name, path);
return NotFound(); return NotFound();
} }

View File

@@ -8,6 +8,7 @@ using Moonlight.Core.Attributes;
using Moonlight.Core.Configuration; using Moonlight.Core.Configuration;
using Moonlight.Core.Database.Entities; using Moonlight.Core.Database.Entities;
using Moonlight.Core.Services; using Moonlight.Core.Services;
using IdentityService = Moonlight.Core.Services.IdentityService;
namespace Moonlight.Core.Http.Controllers; namespace Moonlight.Core.Http.Controllers;
@@ -19,15 +20,17 @@ public class AvatarController : Controller
private readonly Repository<User> UserRepository; private readonly Repository<User> UserRepository;
private readonly ConfigService<CoreConfiguration> ConfigService; private readonly ConfigService<CoreConfiguration> ConfigService;
private readonly IdentityService IdentityService; private readonly IdentityService IdentityService;
private readonly ILogger<AvatarController> Logger;
public AvatarController( public AvatarController(
Repository<User> userRepository, Repository<User> userRepository,
IdentityService identityService, IdentityService identityService,
ConfigService<CoreConfiguration> configService) ConfigService<CoreConfiguration> configService, ILogger<AvatarController> logger)
{ {
UserRepository = userRepository; UserRepository = userRepository;
IdentityService = identityService; IdentityService = identityService;
ConfigService = configService; ConfigService = configService;
Logger = logger;
} }
[HttpGet] [HttpGet]
@@ -88,11 +91,10 @@ public class AvatarController : Controller
catch (Exception e) catch (Exception e)
{ {
if(e is HttpRequestException requestException && requestException.InnerException is IOException ioException) if(e is HttpRequestException requestException && requestException.InnerException is IOException ioException)
Logger.Warn($"Unable to fetch gravatar for user {user.Id}. Is moonlight inside a proxy requiring network?: {ioException.Message}"); Logger.LogWarning("Unable to fetch gravatar for user {userId}. Is moonlight inside a proxy requiring network?: {message}", user.Id, ioException.Message);
else else
{ {
Logger.Warn($"Unable to fetch gravatar for user {user.Id}"); Logger.LogWarning("Unable to fetch gravatar for user {userId}: {e}", user.Id, e);
Logger.Warn(e);
} }
return new MemoryStream(); return new MemoryStream();

View File

@@ -4,16 +4,18 @@ namespace Moonlight.Core.Http.Middleware;
public class DebugLogMiddleware public class DebugLogMiddleware
{ {
private readonly ILogger<DebugLogMiddleware> Logger;
private RequestDelegate Next; private RequestDelegate Next;
public DebugLogMiddleware(RequestDelegate next) public DebugLogMiddleware(RequestDelegate next, ILogger<DebugLogMiddleware> logger)
{ {
Next = next; Next = next;
Logger = logger;
} }
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
Logger.Debug($"[{context.Request.Method.ToUpper()}] {context.Request.Path}"); Logger.LogDebug("[{method}] {path}", context.Request.Method.ToUpper(), context.Request.Path);
await Next(context); await Next(context);
} }

View File

@@ -1,9 +1,9 @@
using MoonCoreUI.Helpers; using MoonCore.Blazor.Helpers;
using Moonlight.Core.Interfaces.Ui.Admin; using Moonlight.Core.Interfaces.Ui.Admin;
using Moonlight.Core.Models.Abstractions; using Moonlight.Core.Models.Abstractions;
using Moonlight.Core.UI.Components.Cards; using Moonlight.Core.UI.Components.Cards;
namespace Moonlight.Core.Implementations.UI.Admin.AdminColumns; namespace Moonlight.Core.Implementations.AdminDashboard;
public class UserCount : IAdminDashboardColumn public class UserCount : IAdminDashboardColumn
{ {

View File

@@ -1,9 +1,9 @@
using MoonCoreUI.Helpers; using MoonCore.Blazor.Helpers;
using Moonlight.Core.Interfaces.UI.User; using Moonlight.Core.Interfaces.UI.User;
using Moonlight.Core.Models.Abstractions; using Moonlight.Core.Models.Abstractions;
using Moonlight.Core.UI.Components.Cards; using Moonlight.Core.UI.Components.Cards;
namespace Moonlight.Core.Implementations.UI.Index; namespace Moonlight.Core.Implementations.UserDashboard;
public class GreetingMessages : IUserDashboardComponent public class GreetingMessages : IUserDashboardComponent
{ {

View File

@@ -9,6 +9,7 @@ public class PreInitContext
public List<Assembly> DiAssemblies { get; set; } = new(); public List<Assembly> DiAssemblies { get; set; } = new();
public Dictionary<string, List<string>> Assets { get; set; } = new(); public Dictionary<string, List<string>> Assets { get; set; } = new();
public PluginService Plugins { get; set; } public PluginService Plugins { get; set; }
public ILoggerFactory LoggerFactory { get; set; }
public void EnableDependencyInjection<T>() public void EnableDependencyInjection<T>()
{ {

View File

@@ -1,4 +1,4 @@
using MoonCoreUI.Components; using MoonCore.Blazor.Components;
namespace Moonlight.Core.Models.Abstractions.Feature; namespace Moonlight.Core.Models.Abstractions.Feature;

View File

@@ -1,5 +1,4 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using MoonCoreUI.Attributes;
namespace Moonlight.Core.Models.Forms; namespace Moonlight.Core.Models.Forms;
@@ -8,12 +7,12 @@ public class ChangePasswordForm
[Required(ErrorMessage = "You need to provide a password")] [Required(ErrorMessage = "You need to provide a password")]
[MinLength(8, ErrorMessage = "The password must be at least 8 characters long")] [MinLength(8, ErrorMessage = "The password must be at least 8 characters long")]
[MaxLength(256, ErrorMessage = "The password must not be longer than 256 characters")] [MaxLength(256, ErrorMessage = "The password must not be longer than 256 characters")]
[CustomFormType(Type = "password")] //TODO: [CustomFormType(Type = "password")]
public string Password { get; set; } public string Password { get; set; }
[Required(ErrorMessage = "You need to provide a password")] [Required(ErrorMessage = "You need to provide a password")]
[MinLength(8, ErrorMessage = "The password must be at least 8 characters long")] [MinLength(8, ErrorMessage = "The password must be at least 8 characters long")]
[MaxLength(256, ErrorMessage = "The password must not be longer than 256 characters")] [MaxLength(256, ErrorMessage = "The password must not be longer than 256 characters")]
[CustomFormType(Type = "password")] //TODO: [CustomFormType(Type = "password")]
public string RepeatedPassword { get; set; } public string RepeatedPassword { get; set; }
} }

View File

@@ -1,6 +1,5 @@
using System.ComponentModel; using System.ComponentModel;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using MoonCoreUI.Attributes;
namespace Moonlight.Core.Models.Forms.Users; namespace Moonlight.Core.Models.Forms.Users;
@@ -17,7 +16,7 @@ public class UpdateUserForm
public string Email { get; set; } public string Email { get; set; }
[Description("This toggles the use of the two factor authentication")] [Description("This toggles the use of the two factor authentication")]
[RadioButtonBool("Enabled", "Disabled", TrueIcon = "bx-lock-alt", FalseIcon = "bx-lock-open-alt")] //TODO: [RadioButtonBool("Enabled", "Disabled", TrueIcon = "bx-lock-alt", FalseIcon = "bx-lock-open-alt")]
[DisplayName("Two factor authentication")] [DisplayName("Two factor authentication")]
public bool Totp { get; set; } = false; public bool Totp { get; set; } = false;
} }

View File

@@ -1,5 +1,5 @@
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using MoonCoreUI.Services; using MoonCore.Blazor.Services;
using Moonlight.Core.Services; using Moonlight.Core.Services;
namespace Moonlight.Core.Models; namespace Moonlight.Core.Models;

View File

@@ -1,8 +1,7 @@
using System.Reflection; using System.Reflection;
using MoonCore.Blazor.Components;
using MoonCore.Extensions; using MoonCore.Extensions;
using MoonCore.Helpers;
using MoonCore.Services; using MoonCore.Services;
using MoonCoreUI.Components;
using Moonlight.Core.Configuration; using Moonlight.Core.Configuration;
using Moonlight.Core.Models.Abstractions.Feature; using Moonlight.Core.Models.Abstractions.Feature;
@@ -16,14 +15,17 @@ public class FeatureService
private readonly List<MoonlightFeature> Features = new(); private readonly List<MoonlightFeature> Features = new();
private readonly ConfigService<CoreConfiguration> ConfigService; private readonly ConfigService<CoreConfiguration> ConfigService;
public FeatureService(ConfigService<CoreConfiguration> configService) private readonly ILogger<FeatureService> Logger;
public FeatureService(ConfigService<CoreConfiguration> configService, ILogger<FeatureService> logger)
{ {
ConfigService = configService; ConfigService = configService;
Logger = logger;
} }
public Task Load() public Task Load()
{ {
Logger.Info("Loading features"); Logger.LogInformation("Loading features");
// TODO: Add dll loading here as well // TODO: Add dll loading here as well
@@ -43,24 +45,25 @@ public class FeatureService
if (feature == null) if (feature == null)
{ {
Logger.Warn($"Unable to construct {featureType.FullName} feature"); Logger.LogWarning("Unable to construct '{name}' feature", featureType.FullName);
continue; continue;
} }
Features.Add(feature); Features.Add(feature);
Logger.Info($"Loaded feature '{feature.Name}' by '{feature.Author}'"); Logger.LogInformation("Loaded feature '{name}' by '{author}'", feature.Name, feature.Author);
} }
return Task.CompletedTask; return Task.CompletedTask;
} }
public async Task PreInit(WebApplicationBuilder builder, PluginService pluginService) public async Task PreInit(WebApplicationBuilder builder, PluginService pluginService, ILoggerFactory preRunLoggerFactory)
{ {
Logger.Info("Pre-initializing features"); Logger.LogInformation("Pre-initializing features");
PreInitContext.Builder = builder; PreInitContext.Builder = builder;
PreInitContext.Plugins = pluginService; PreInitContext.Plugins = pluginService;
PreInitContext.LoggerFactory = preRunLoggerFactory;
foreach (var feature in Features) foreach (var feature in Features)
{ {
@@ -70,8 +73,7 @@ public class FeatureService
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Error($"An error occured while performing pre init for feature '{feature.Name}'"); Logger.LogError("An error occured while performing pre init for feature '{name}': {e}", feature.Name, e);
Logger.Error(e);
} }
} }
@@ -82,7 +84,7 @@ public class FeatureService
public async Task Init(WebApplication application) public async Task Init(WebApplication application)
{ {
Logger.Info("Initializing features"); Logger.LogInformation("Initializing features");
var initContext = new InitContext() var initContext = new InitContext()
{ {
@@ -97,15 +99,14 @@ public class FeatureService
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Error($"An error occured while performing init for feature '{feature.Name}'"); Logger.LogError("An error occured while performing init for feature '{name}': {e}", feature.Name, e);
Logger.Error(e);
} }
} }
} }
public async Task UiInit() public async Task UiInit()
{ {
Logger.Info("Initializing feature uis"); Logger.LogInformation("Initializing feature uis");
foreach (var feature in Features) foreach (var feature in Features)
{ {
@@ -115,8 +116,7 @@ public class FeatureService
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Error($"An error occured while performing ui init for feature '{feature.Name}'"); Logger.LogError("An error occured while performing ui init for feature '{name}': {e}", feature.Name, e);
Logger.Error(e);
} }
} }
} }
@@ -138,8 +138,7 @@ public class FeatureService
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Error($"An error occured while performing session init for feature '{feature.Name}'"); Logger.LogError("An error occured while performing session init for feature '{name}': {e}", feature.Name, e);
Logger.Error(e);
} }
} }
} }
@@ -159,8 +158,7 @@ public class FeatureService
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Error($"An error occured while performing session dispose for feature '{feature.Name}'"); Logger.LogError("An error occured while performing session dispose for feature '{name}': {e}", feature.Name, e);
Logger.Error(e);
} }
} }
} }

View File

@@ -11,11 +11,13 @@ public class HotKeyService
private readonly IJSRuntime JsRuntime; private readonly IJSRuntime JsRuntime;
private readonly List<HotKeyModel> HotKeys = new(); private readonly List<HotKeyModel> HotKeys = new();
public SmartEventHandler<string> HotKeyPressed { get; set; } = new(); public SmartEventHandler<string> HotKeyPressed { get; set; }
public HotKeyService(IJSRuntime jsRuntime) public HotKeyService(IJSRuntime jsRuntime, ILogger<SmartEventHandler> eventHandlerLogger)
{ {
JsRuntime = jsRuntime; JsRuntime = jsRuntime;
HotKeyPressed = new(eventHandlerLogger);
} }
public async Task RegisterHotkey(string key, string modifier, string action) public async Task RegisterHotkey(string key, string modifier, string action)

View File

@@ -15,7 +15,7 @@ public class IdentityService : IDisposable
public User? CurrentUserNullable { get; private set; } public User? CurrentUserNullable { get; private set; }
public User CurrentUser => CurrentUserNullable!; public User CurrentUser => CurrentUserNullable!;
public bool IsLoggedIn => CurrentUserNullable != null; public bool IsLoggedIn => CurrentUserNullable != null;
public SmartEventHandler OnAuthenticationStateChanged { get; set; } = new(); public SmartEventHandler OnAuthenticationStateChanged { get; set; }
private string Token = ""; private string Token = "";
@@ -26,11 +26,14 @@ public class IdentityService : IDisposable
public IdentityService( public IdentityService(
JwtService<CoreJwtType> jwtService, JwtService<CoreJwtType> jwtService,
ConfigService<CoreConfiguration> configService, ConfigService<CoreConfiguration> configService,
Repository<User> userRepository) Repository<User> userRepository,
ILogger<SmartEventHandler> eventHandlerLogger)
{ {
JwtService = jwtService; JwtService = jwtService;
ConfigService = configService; ConfigService = configService;
UserRepository = userRepository; UserRepository = userRepository;
OnAuthenticationStateChanged = new(eventHandlerLogger);
} }
public async Task<string> Authenticate(User user) public async Task<string> Authenticate(User user)

View File

@@ -10,10 +10,18 @@ public class MoonlightService
public WebApplication Application { get; set; } // Do NOT modify using a plugin public WebApplication Application { get; set; } // Do NOT modify using a plugin
private readonly DateTime StartTimestamp = DateTime.UtcNow; private readonly DateTime StartTimestamp = DateTime.UtcNow;
private readonly CoreEvents CoreEvents;
private readonly ILogger<MoonlightService> Logger;
public MoonlightService(CoreEvents coreEvents, ILogger<MoonlightService> logger)
{
CoreEvents = coreEvents;
Logger = logger;
}
public async Task Restart() public async Task Restart()
{ {
Logger.Info("Restarting moonlight"); Logger.LogInformation("Restarting moonlight");
// Notify all users that this instance will restart // Notify all users that this instance will restart
await CoreEvents.OnMoonlightRestart.Invoke(); await CoreEvents.OnMoonlightRestart.Invoke();

View File

@@ -9,8 +9,12 @@ public class PluginService
private readonly Dictionary<Type, List<object>> ImplementationCache = new(); private readonly Dictionary<Type, List<object>> ImplementationCache = new();
private readonly List<MoonlightPlugin> Plugins = new(); private readonly List<MoonlightPlugin> Plugins = new();
public PluginService() private readonly ILogger<PluginService> Logger;
public PluginService(ILogger<PluginService> logger)
{ {
Logger = logger;
Directory.CreateDirectory(PathBuilder.Dir("storage", "plugins")); Directory.CreateDirectory(PathBuilder.Dir("storage", "plugins"));
} }
@@ -136,7 +140,7 @@ public class PluginService
if (pluginTypes.Length == 0) if (pluginTypes.Length == 0)
{ {
Logger.Info($"Loaded assembly as library. {dllFile}"); Logger.LogInformation("Loaded assembly as library: {dllFile}", dllFile);
continue; continue;
} }
@@ -146,19 +150,17 @@ public class PluginService
{ {
var plugin = await LoadFromType(pluginType); var plugin = await LoadFromType(pluginType);
Logger.Info($"Loaded plugin '{plugin.Name}'. Created by '{plugin.Author}'"); Logger.LogInformation("Loaded plugin '{name}'. Created by '{author}'", plugin.Name, plugin.Author);
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Fatal($"An error occured while loading plugin '{pluginType.FullName}'"); Logger.LogError("An error occured while loading plugin '{name}': {e}", pluginType.FullName, e);
Logger.Fatal(e);
} }
} }
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Fatal($"An error occured while loading assembly '{dllFile}'"); Logger.LogError("An error occured while loading assembly '{dllFile}': {e}", dllFile, e);
Logger.Fatal(e);
} }
} }
} }

View File

@@ -9,10 +9,12 @@ public class StartupJobService : BackgroundService
{ {
private readonly List<StartupJob> Jobs = new(); private readonly List<StartupJob> Jobs = new();
private readonly IServiceProvider ServiceProvider; private readonly IServiceProvider ServiceProvider;
private readonly ILogger<StartupJobService> Logger;
public StartupJobService(IServiceProvider serviceProvider) public StartupJobService(IServiceProvider serviceProvider, ILogger<StartupJobService> logger)
{ {
ServiceProvider = serviceProvider; ServiceProvider = serviceProvider;
Logger = logger;
} }
public Task AddJob(string name, TimeSpan delay, Func<IServiceProvider, Task> action) public Task AddJob(string name, TimeSpan delay, Func<IServiceProvider, Task> action)
@@ -29,7 +31,7 @@ public class StartupJobService : BackgroundService
public override Task Run() public override Task Run()
{ {
Logger.Info("Running startup jobs"); Logger.LogInformation("Running startup jobs");
foreach (var job in Jobs) foreach (var job in Jobs)
{ {
@@ -42,8 +44,7 @@ public class StartupJobService : BackgroundService
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warn($"The startup job '{job.Name}' failed:"); Logger.LogWarning("The startup job '{name}' failed: {e}", job.Name, e);
Logger.Warn(e);
} }
}); });
} }

View File

@@ -11,13 +11,17 @@ namespace Moonlight.Core.Services;
[Scoped] [Scoped]
public class UnloadService public class UnloadService
{ {
public SmartEventHandler OnUnloaded { get; set; } = new(); public SmartEventHandler OnUnloaded { get; set; }
private readonly IJSRuntime JsRuntime; private readonly IJSRuntime JsRuntime;
private readonly ILogger<UnloadService> Logger;
public UnloadService(IJSRuntime jsRuntime) public UnloadService(IJSRuntime jsRuntime, ILogger<SmartEventHandler> eventHandlerLogger, ILogger<UnloadService> logger)
{ {
JsRuntime = jsRuntime; JsRuntime = jsRuntime;
Logger = logger;
OnUnloaded = new(eventHandlerLogger);
} }
public async Task Initialize() public async Task Initialize()
@@ -28,8 +32,7 @@ public class UnloadService
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Error("An error occured while registering unload event handler"); Logger.LogError("An error occured while registering unload event handler: {e}", e);
Logger.Error(e);
} }
} }

View File

@@ -5,7 +5,6 @@
@using Moonlight.Core.Models.Forms @using Moonlight.Core.Models.Forms
@using Moonlight.Core.Services @using Moonlight.Core.Services
@using MoonCore.Exceptions @using MoonCore.Exceptions
@using MoonCoreUI.Services
@inject IAuthenticationProvider AuthenticationProvider @inject IAuthenticationProvider AuthenticationProvider
@inject IdentityService IdentityService @inject IdentityService IdentityService
@@ -93,7 +92,7 @@
var token = await IdentityService.Authenticate(user); var token = await IdentityService.Authenticate(user);
// Save token for later use // Save token for later use
await CookieService.SetValue("token", token); await CookieService.SetValue("token", token, 30); //TODO: Add days to config option
// Forward the user if not on the specific page // Forward the user if not on the specific page
if(new Uri(Navigation.Uri).LocalPath.StartsWith("/login")) if(new Uri(Navigation.Uri).LocalPath.StartsWith("/login"))

View File

@@ -7,8 +7,8 @@
@using Moonlight.Core.Services @using Moonlight.Core.Services
@using MoonCore.Exceptions @using MoonCore.Exceptions
@using MoonCore.Services @using MoonCore.Services
@using MoonCoreUI.Services
@using Moonlight.Core.Configuration @using Moonlight.Core.Configuration
@using IdentityService = Moonlight.Core.Services.IdentityService
@inject IAuthenticationProvider AuthenticationProvider @inject IAuthenticationProvider AuthenticationProvider
@inject IdentityService IdentityService @inject IdentityService IdentityService
@@ -99,7 +99,7 @@
var token = await IdentityService.Authenticate(user); var token = await IdentityService.Authenticate(user);
// Save token for later use // Save token for later use
await CookieService.SetValue("token", token); await CookieService.SetValue("token", token, 30); // TODO: config
// Forward the user if not on the specific page // Forward the user if not on the specific page
if(new Uri(Navigation.Uri).LocalPath.StartsWith("/register")) if(new Uri(Navigation.Uri).LocalPath.StartsWith("/register"))

View File

@@ -139,7 +139,7 @@ else
private string Key = ""; private string Key = "";
private TwoFactorCodeForm CodeForm = new(); private TwoFactorCodeForm CodeForm = new();
private async Task Disable() private async Task Disable(ConfirmButton _)
{ {
await AuthenticationProvider.SetTwoFactorSecret(IdentityService.CurrentUser, ""); await AuthenticationProvider.SetTwoFactorSecret(IdentityService.CurrentUser, "");
await IdentityService.Authenticate(true); await IdentityService.Authenticate(true);

View File

@@ -1,6 +1,7 @@
@using MoonCore.Services @using MoonCore.Services
@using Moonlight.Core.Configuration @using Moonlight.Core.Configuration
@using Moonlight.Core.Services @using Moonlight.Core.Services
@using IdentityService = Moonlight.Core.Services.IdentityService
@inject IdentityService IdentityService @inject IdentityService IdentityService
@inject ConfigService<CoreConfiguration> ConfigService @inject ConfigService<CoreConfiguration> ConfigService

View File

@@ -1,5 +1,4 @@
@using Moonlight.Core.Services @using Moonlight.Core.Services
@using MoonCoreUI.Services
@inject IdentityService IdentityService @inject IdentityService IdentityService
@inject CookieService CookieService @inject CookieService CookieService
@@ -94,7 +93,7 @@
private async Task Logout() private async Task Logout()
{ {
// Reset token // Reset token
await CookieService.SetValue("token", ""); await CookieService.SetValue("token", "", 30);
// Reset token in identity service // Reset token in identity service
await IdentityService.Authenticate(""); await IdentityService.Authenticate("");

View File

@@ -2,6 +2,7 @@
@using MoonCore.Services @using MoonCore.Services
@using Moonlight.Core.Configuration @using Moonlight.Core.Configuration
@using Moonlight.Core.Services @using Moonlight.Core.Services
@using IdentityService = Moonlight.Core.Services.IdentityService
@inject ConfigService<CoreConfiguration> ConfigService @inject ConfigService<CoreConfiguration> ConfigService
@inject IdentityService IdentityService @inject IdentityService IdentityService

View File

@@ -4,6 +4,7 @@
@using Moonlight.Core.Services @using Moonlight.Core.Services
@inject IdentityService IdentityService @inject IdentityService IdentityService
@inject ILogger<SoftErrorHandler> Logger
@inherits ErrorBoundaryBase @inherits ErrorBoundaryBase
@@ -75,8 +76,7 @@ else
Crashed = true; Crashed = true;
var username = IdentityService.IsLoggedIn ? IdentityService.CurrentUser.Username : "Guest"; var username = IdentityService.IsLoggedIn ? IdentityService.CurrentUser.Username : "Guest";
Logger.Warn($"A crash occured in the view of '{username}'"); Logger.LogWarning("A crash occured in the view of '{username}': {exception}", username, exception);
Logger.Warn(exception);
} }
Recover(); Recover();

View File

@@ -3,6 +3,7 @@
@using MoonCore.Services @using MoonCore.Services
@using Moonlight.Core.Configuration @using Moonlight.Core.Configuration
@using Moonlight.Core.Events @using Moonlight.Core.Events
@using IdentityService = Moonlight.Core.Services.IdentityService
@inherits LayoutComponentBase @inherits LayoutComponentBase
@@ -13,6 +14,7 @@
@inject UnloadService UnloadService @inject UnloadService UnloadService
@inject ConfigService<CoreConfiguration> ConfigService @inject ConfigService<CoreConfiguration> ConfigService
@inject ScopedStorageService ScopedStorageService @inject ScopedStorageService ScopedStorageService
@inject CoreEvents CoreEvents
@implements IDisposable @implements IDisposable
@@ -99,6 +101,9 @@
</div> </div>
</div> </div>
<ModalLaunchPoint />
<ToastLaunchPoint />
@code @code
{ {
private bool IsInitialized = false; private bool IsInitialized = false;

View File

@@ -3,7 +3,6 @@
@using Moonlight.Core.Services @using Moonlight.Core.Services
@using Moonlight.Core.Models.Forms @using Moonlight.Core.Models.Forms
@using Mappy.Net @using Mappy.Net
@using MoonCoreUI.Services
@using Moonlight.Core.Models.Abstractions @using Moonlight.Core.Models.Abstractions
@using Moonlight.Core.UI.Components.Navigations @using Moonlight.Core.UI.Components.Navigations
@@ -23,7 +22,7 @@
<AccountNavigation Index="0"/> <AccountNavigation Index="0"/>
<LazyLoader Load="Load" ShowAsCard="true"> <LazyLoader Load="Load">
<div class="row g-5"> <div class="row g-5">
<div class="col-md-3 col-12"> <div class="col-md-3 col-12">

View File

@@ -3,7 +3,6 @@
@using Moonlight.Core.Services @using Moonlight.Core.Services
@using Moonlight.Core.UI.Components.Navigations @using Moonlight.Core.UI.Components.Navigations
@using Moonlight.Core.Models.Forms @using Moonlight.Core.Models.Forms
@using MoonCoreUI.Services
@using Moonlight.Core.Models.Abstractions @using Moonlight.Core.Models.Abstractions
@using MoonCore.Exceptions @using MoonCore.Exceptions
@using Moonlight.Core.UI.Components.Auth @using Moonlight.Core.UI.Components.Auth

View File

@@ -21,11 +21,11 @@
<div class="mt-5"> <div class="mt-5">
<div class="card card-body py-3 px-5"> <div class="card card-body py-3 px-5">
<LazyLoader Load="LoadApis"> <LazyLoader Load="LoadApis">
<CrudTable TItem="ApiModel" ItemSource="Apis" PageSize="100" ShowPagination="false"> <MCBTable TItem="ApiModel" ItemSource="Apis" PageSize="100" ShowPagination="false">
<CrudColumn TItem="ApiModel" Field="@(x => x.Id)" Title="Id" /> <MCBColumn TItem="ApiModel" Field="@(x => x.Id)" Title="Id" />
<CrudColumn TItem="ApiModel" Field="@(x => x.Name)" Title="Name" /> <MCBColumn TItem="ApiModel" Field="@(x => x.Name)" Title="Name" />
<CrudColumn TItem="ApiModel" Field="@(x => x.Version)" Title="Version" /> <MCBColumn TItem="ApiModel" Field="@(x => x.Version)" Title="Version" />
<CrudColumn TItem="ApiModel"> <MCBColumn TItem="ApiModel">
<Template> <Template>
<div class="text-end"> <div class="text-end">
@if (ConfigService.Get().Development.EnableApiReference) @if (ConfigService.Get().Development.EnableApiReference)
@@ -44,8 +44,8 @@
} }
</div> </div>
</Template> </Template>
</CrudColumn> </MCBColumn>
</CrudTable> </MCBTable>
</LazyLoader> </LazyLoader>
</div> </div>
</div> </div>

View File

@@ -1,8 +1,8 @@
@page "/admin/api/keys" @page "/admin/api/keys"
@using MoonCore.Abstractions @using MoonCore.Abstractions
@using MoonCore.Blazor.Models.Forms
@using MoonCore.Helpers @using MoonCore.Helpers
@using MoonCoreUI.Services
@using Moonlight.Core.Database.Entities @using Moonlight.Core.Database.Entities
@using Moonlight.Core.Models.Forms.ApiKeys @using Moonlight.Core.Models.Forms.ApiKeys
@using Moonlight.Core.UI.Components.Navigations @using Moonlight.Core.UI.Components.Navigations
@@ -19,9 +19,9 @@
TCreateForm="CreateApiKeyForm" TCreateForm="CreateApiKeyForm"
TUpdateForm="UpdateApiKeyForm" TUpdateForm="UpdateApiKeyForm"
Loader="ApiKeysLoader" Loader="ApiKeysLoader"
ValidateAdd="ValidateAdd"> OnConfigure="OnConfigure">
<View> <View>
<CrudColumn TItem="ApiKey" Field="@(x => x.Key)" Title="Key"> <MCBColumn TItem="ApiKey" Field="@(x => x.Key)" Title="Key">
<Template> <Template>
@{ @{
var apiKeyHalf = Formatter.CutInHalf(context!.Key); var apiKeyHalf = Formatter.CutInHalf(context!.Key);
@@ -35,19 +35,19 @@
</span> </span>
</div> </div>
</Template> </Template>
</CrudColumn> </MCBColumn>
<CrudColumn TItem="ApiKey" Field="@(x => x.Description)" Title="Description"/> <MCBColumn TItem="ApiKey" Field="@(x => x.Description)" Title="Description"/>
<CrudColumn TItem="ApiKey" Field="@(x => x.CreatedAt)" Title="Created at"> <MCBColumn TItem="ApiKey" Field="@(x => x.CreatedAt)" Title="Created at">
<Template> <Template>
@Formatter.FormatDate(context!.CreatedAt) @Formatter.FormatDate(context!.CreatedAt)
</Template> </Template>
</CrudColumn> </MCBColumn>
<CrudColumn TItem="ApiKey" Field="@(x => x.ExpiresAt)" Title="Expires at"> <MCBColumn TItem="ApiKey" Field="@(x => x.ExpiresAt)" Title="Expires at">
<Template> <Template>
@Formatter.FormatDate(context!.ExpiresAt) @Formatter.FormatDate(context!.ExpiresAt)
</Template> </Template>
</CrudColumn> </MCBColumn>
<CrudColumn TItem="ApiKey" Field="@(x => x.PermissionJson)" Title="Permissions"/> <MCBColumn TItem="ApiKey" Field="@(x => x.PermissionJson)" Title="Permissions"/>
</View> </View>
</AutoCrud> </AutoCrud>
</div> </div>
@@ -59,12 +59,15 @@
return repository.Get(); return repository.Get();
} }
private async Task ValidateAdd(ApiKey apiKey) private void OnConfigure(AutoCrudConfiguration<ApiKey> configuration)
{
configuration.ValidateAdd = async apiKey =>
{ {
var key = Formatter.GenerateString(32); var key = Formatter.GenerateString(32);
apiKey.Key = key; apiKey.Key = key;
await ClipboardService.Copy(key); await ClipboardService.Copy(key);
await ToastService.Info("Copied api key into your clipboard"); await ToastService.Info("Copied api key into your clipboard");
};
} }
} }

View File

@@ -1,10 +1,9 @@
@page "/admin/sys/diagnose" @page "/admin/sys/diagnose"
@using Moonlight.Core.UI.Components.Navigations @using Moonlight.Core.UI.Components.Navigations
@using MoonCoreUI.Services
@using Moonlight.Core.Services @using Moonlight.Core.Services
@inject FileDownloadService DownloadService @inject DownloadService DownloadService
@inject DiagnoseService DiagnoseService @inject DiagnoseService DiagnoseService
@attribute [RequirePermission(9999)] @attribute [RequirePermission(9999)]
@@ -31,7 +30,7 @@
</div> </div>
<div class="col-md-6 col-12"> <div class="col-md-6 col-12">
<div class="card card-body p-5"> <div class="card card-body p-5">
<WButton OnClick="DownloadReport" Text="Download diagnose report" CssClasses="btn btn-primary"/> <WButton OnClick="DownloadReport" CssClasses="btn btn-primary">Download diagnose report</WButton>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -6,6 +6,7 @@
@using MoonCore.Helpers @using MoonCore.Helpers
@inject MoonlightService MoonlightService @inject MoonlightService MoonlightService
@inject HostSystemHelper HostSystemHelper
@attribute [RequirePermission(9999)] @attribute [RequirePermission(9999)]
@@ -55,7 +56,7 @@
<i class="bx bx-lg text-white align-middle bx-refresh"></i> <i class="bx bx-lg text-white align-middle bx-refresh"></i>
</div> </div>
<div class="col-9 d-flex align-items-center"> <div class="col-9 d-flex align-items-center">
<ConfirmButton OnClick="MoonlightService.Restart" Text="Restart" CssClasses="btn btn-lg btn-danger w-100"/> <ConfirmButton OnClick="_ => MoonlightService.Restart()" CssClasses="btn btn-lg btn-danger w-100">Restart</ConfirmButton>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -2,7 +2,6 @@
@using Moonlight.Core.UI.Components.Navigations @using Moonlight.Core.UI.Components.Navigations
@using MoonCore.Abstractions @using MoonCore.Abstractions
@using MoonCoreUI.Services
@using Moonlight.Core.Database.Entities @using Moonlight.Core.Database.Entities
@using Moonlight.Core.Services @using Moonlight.Core.Services
@@ -17,7 +16,7 @@
<LazyLoader Load="Load"> <LazyLoader Load="Load">
<div class="card card-body p-5"> <div class="card card-body p-5">
<div class="input-group"> <div class="input-group">
<SmartDropdown @bind-Value="SelectedUser" <MCBDropdown @bind-Value="SelectedUser"
Items="Users" Items="Users"
Placeholder="Select a user" Placeholder="Select a user"
DisplayFunc="@(x => x.Username)" DisplayFunc="@(x => x.Username)"
@@ -58,14 +57,7 @@
</div> </div>
<div class="card card-body p-5 d-flex justify-content-center mt-5"> <div class="card card-body p-5 d-flex justify-content-center mt-5">
@if (SelectedUser == null) <WButton OnClick="SavePermissions" CssClasses="btn btn-primary">Apply</WButton>
{
<button class="btn btn-primary disabled" disabled="">Apply</button>
}
else
{
<WButton OnClick="SavePermissions" CssClasses="btn btn-primary" Text="Apply"/>
}
</div> </div>
} }
</LazyLoader> </LazyLoader>

View File

@@ -3,8 +3,6 @@
@using Moonlight.Core.UI.Components.Navigations @using Moonlight.Core.UI.Components.Navigations
@using System.Reflection @using System.Reflection
@using MoonCore.Services @using MoonCore.Services
@using MoonCoreUI.Services
@using MoonCoreUI.Helpers
@using Moonlight.Core.Configuration @using Moonlight.Core.Configuration
@inject ConfigService<CoreConfiguration> ConfigService @inject ConfigService<CoreConfiguration> ConfigService
@@ -85,6 +83,8 @@ else
<div class="card card-body"> <div class="card card-body">
<div class="row g-5"> <div class="row g-5">
<LazyLoader @ref="LazyLoader" Load="Load"> <LazyLoader @ref="LazyLoader" Load="Load">
<AutoFormGenerator Model="ModelToShow" />
@*
@foreach (var prop in Properties) @foreach (var prop in Properties)
{ {
<div class="col-md-6 col-12"> <div class="col-md-6 col-12">
@@ -99,7 +99,7 @@ else
@rf @rf
</div> </div>
} }*@
</LazyLoader> </LazyLoader>
</div> </div>
</div> </div>

View File

@@ -3,9 +3,7 @@
@using Moonlight.Core.UI.Components.Navigations @using Moonlight.Core.UI.Components.Navigations
@using MoonCore.Abstractions @using MoonCore.Abstractions
@using Moonlight.Core.Database.Entities @using Moonlight.Core.Database.Entities
@using BlazorTable
@using MoonCore.Exceptions @using MoonCore.Exceptions
@using MoonCoreUI.Services
@using Moonlight.Core.Models.Abstractions @using Moonlight.Core.Models.Abstractions
@using Moonlight.Core.Models.Forms.Users @using Moonlight.Core.Models.Forms.Users
@@ -23,17 +21,17 @@
CustomAdd="Add" CustomAdd="Add"
ValidateUpdate="ValidateUpdate"> ValidateUpdate="ValidateUpdate">
<View> <View>
<CrudColumn TItem="User" Field="@(x => x.Id)" Title="Id" Filterable="true"/> <MCBColumn TItem="User" Field="@(x => x.Id)" Title="Id" Filterable="true"/>
<CrudColumn TItem="User" Field="@(x => x.Email)" Title="Email" Filterable="true"/> <MCBColumn TItem="User" Field="@(x => x.Email)" Title="Email" Filterable="true"/>
<CrudColumn TItem="User" Field="@(x => x.Username)" Title="Username" Filterable="true"/> <MCBColumn TItem="User" Field="@(x => x.Username)" Title="Username" Filterable="true"/>
<CrudColumn TItem="User" Field="@(x => x.CreatedAt)" Title="Created at"/> <MCBColumn TItem="User" Field="@(x => x.CreatedAt)" Title="Created at"/>
</View> </View>@*
<UpdateActions> <UpdateActions>
<WButton OnClick="() => ChangePassword(context)" CssClasses="btn btn-info me-2"> <WButton OnClick="() => ChangePassword(context)" CssClasses="btn btn-info me-2">
<i class="bx bx-sm bxs-key"></i> <i class="bx bx-sm bxs-key"></i>
Change password Change password
</WButton> </WButton>
</UpdateActions> </UpdateActions>*@
</AutoCrud> </AutoCrud>
@code @code
@@ -45,13 +43,14 @@
private async Task ChangePassword(User user) private async Task ChangePassword(User user)
{ {
var newPassword = await AlertService.Text($"Enter a new password for {user.Username}", ""); await AlertService.Text("", "Enter a new password for {user.Username}", async newPassword =>
{
// This handles empty and canceled input // This handles empty and canceled input
if (string.IsNullOrEmpty(newPassword)) if (string.IsNullOrEmpty(newPassword))
return; return;
await AuthenticationProvider.ChangePassword(user, newPassword); await AuthenticationProvider.ChangePassword(user, newPassword);
});
} }
private async Task Add(User user) private async Task Add(User user)

View File

@@ -3,7 +3,6 @@
@using Moonlight.Core.UI.Components.Navigations @using Moonlight.Core.UI.Components.Navigations
@using Moonlight.Core.Services @using Moonlight.Core.Services
@using MoonCore.Helpers @using MoonCore.Helpers
@using MoonCoreUI.Services
@using Moonlight.Core.Models @using Moonlight.Core.Models
@inject SessionService SessionService @inject SessionService SessionService
@@ -24,11 +23,11 @@
<div class="card mt-5"> <div class="card mt-5">
<div class="card-body px-6 py-4"> <div class="card-body px-6 py-4">
<LazyLoader Load="Load"> <LazyLoader Load="Load">
<CrudTable @ref="Table" <MCBTable @ref="Table"
TItem="Session" TItem="Session"
ItemSource="SessionService.Sessions" ItemSource="SessionService.Sessions"
PageSize="50"> PageSize="50">
<CrudColumn TItem="Session" Title="User" Field="@(x => x.CreatedAt)"> <MCBColumn TItem="Session" Title="User" Field="@(x => x.CreatedAt)">
<Template> <Template>
@if (context.IdentityService.IsLoggedIn) @if (context.IdentityService.IsLoggedIn)
{ {
@@ -41,25 +40,25 @@
<span>Guest</span> <span>Guest</span>
} }
</Template> </Template>
</CrudColumn> </MCBColumn>
<CrudColumn TItem="Session" Title="URL" Field="@(x => x.CreatedAt)"> <MCBColumn TItem="Session" Title="URL" Field="@(x => x.CreatedAt)">
<Template> <Template>
<a target="_blank" href="@(context.NavigationManager.Uri)"> <a target="_blank" href="@(context.NavigationManager.Uri)">
@(context.NavigationManager.Uri) @(context.NavigationManager.Uri)
</a> </a>
</Template> </Template>
</CrudColumn> </MCBColumn>
<CrudColumn TItem="Session" Title="Last activity" Field="@(x => x.UpdatedAt)" Filterable="true"> <MCBColumn TItem="Session" Title="Last activity" Field="@(x => x.UpdatedAt)" Filterable="true">
<Template> <Template>
<span>@(Formatter.FormatUptime(DateTime.UtcNow - context.UpdatedAt))</span> <span>@(Formatter.FormatUptime(DateTime.UtcNow - context.UpdatedAt))</span>
</Template> </Template>
</CrudColumn> </MCBColumn>
<CrudColumn TItem="Session" Title="Connected since" Field="@(x => x.CreatedAt)" Filterable="true"> <MCBColumn TItem="Session" Title="Connected since" Field="@(x => x.CreatedAt)" Filterable="true">
<Template> <Template>
<span>@(Formatter.FormatUptime(DateTime.UtcNow - context.CreatedAt))</span> <span>@(Formatter.FormatUptime(DateTime.UtcNow - context.CreatedAt))</span>
</Template> </Template>
</CrudColumn> </MCBColumn>
<CrudColumn TItem="Session" Title="Actions"> <MCBColumn TItem="Session" Title="Actions">
<Template> <Template>
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
<div class="btn btn-group"> <div class="btn btn-group">
@@ -68,8 +67,8 @@
</div> </div>
</div> </div>
</Template> </Template>
</CrudColumn> </MCBColumn>
</CrudTable> </MCBTable>
</LazyLoader> </LazyLoader>
</div> </div>
</div> </div>
@@ -77,7 +76,7 @@
@code @code
{ {
private CrudTable<Session>? Table; private MCBTable<Session>? Table;
private Timer? UpdateTimer; private Timer? UpdateTimer;
private Task Load(LazyLoader _) private Task Load(LazyLoader _)
@@ -93,8 +92,8 @@
private async Task Redirect(Session session) private async Task Redirect(Session session)
{ {
var url = await AlertService.Text("Enter the target url to redirect to"); await AlertService.Text("Redirect to", "Enter the target url to redirect to", async url =>
{
if (string.IsNullOrEmpty(url)) if (string.IsNullOrEmpty(url))
return; return;
@@ -108,12 +107,13 @@
{ {
await ToastService.Danger("Unable to redirect user. The user is probably no longer connect with moonlight"); await ToastService.Danger("Unable to redirect user. The user is probably no longer connect with moonlight");
} }
});
} }
private async Task Message(Session session) private async Task Message(Session session)
{ {
var message = await AlertService.Text("Enter the message you want to send"); await AlertService.Text("Send message", "Enter the message you want to send", async message =>
{
if (string.IsNullOrEmpty(message)) if (string.IsNullOrEmpty(message))
return; return;
@@ -127,6 +127,7 @@
{ {
await ToastService.Danger("Unable to send message. The user is probably no longer connect with moonlight"); await ToastService.Danger("Unable to send message. The user is probably no longer connect with moonlight");
} }
});
} }
public void Dispose() public void Dispose()

View File

@@ -24,7 +24,13 @@ public class FileManagerFeature : MoonlightFeature
// //
var config = new ConfigService<CoreConfiguration>(PathBuilder.File("storage", "configs", "core.json")); var config = new ConfigService<CoreConfiguration>(PathBuilder.File("storage", "configs", "core.json"));
context.Builder.Services.AddSingleton(new JwtService<FileManagerJwtType>(config.Get().Security.Token));
context.Builder.Services.AddSingleton(
new JwtService<FileManagerJwtType>(
config.Get().Security.Token,
context.LoggerFactory.CreateLogger<JwtService<FileManagerJwtType>>()
)
);
context.AddAsset("FileManager", "js/filemanager.js"); context.AddAsset("FileManager", "js/filemanager.js");
context.AddAsset("FileManager", "editor/ace.css"); context.AddAsset("FileManager", "editor/ace.css");

View File

@@ -13,11 +13,13 @@ public class DownloadController : Controller
{ {
private readonly JwtService<FileManagerJwtType> JwtService; private readonly JwtService<FileManagerJwtType> JwtService;
private readonly SharedFileAccessService SharedFileAccessService; private readonly SharedFileAccessService SharedFileAccessService;
private readonly ILogger<DownloadController> Logger;
public DownloadController(JwtService<FileManagerJwtType> jwtService, SharedFileAccessService sharedFileAccessService) public DownloadController(JwtService<FileManagerJwtType> jwtService, SharedFileAccessService sharedFileAccessService, ILogger<DownloadController> logger)
{ {
JwtService = jwtService; JwtService = jwtService;
SharedFileAccessService = sharedFileAccessService; SharedFileAccessService = sharedFileAccessService;
Logger = logger;
} }
[HttpGet] [HttpGet]
@@ -25,7 +27,7 @@ public class DownloadController : Controller
{ {
if (name.Contains("..")) if (name.Contains(".."))
{ {
Logger.Warn($"A user tried to access a file via path transversal. Name: {name}"); Logger.LogWarning("A user tried to access a file via path transversal. Name: {name}", name);
return NotFound(); return NotFound();
} }

View File

@@ -13,13 +13,15 @@ public class UploadController : Controller
{ {
private readonly JwtService<FileManagerJwtType> JwtService; private readonly JwtService<FileManagerJwtType> JwtService;
private readonly SharedFileAccessService SharedFileAccessService; private readonly SharedFileAccessService SharedFileAccessService;
private readonly ILogger<UploadController> Logger;
public UploadController( public UploadController(
JwtService<FileManagerJwtType> jwtService, JwtService<FileManagerJwtType> jwtService,
SharedFileAccessService sharedFileAccessService) SharedFileAccessService sharedFileAccessService, ILogger<UploadController> logger)
{ {
JwtService = jwtService; JwtService = jwtService;
SharedFileAccessService = sharedFileAccessService; SharedFileAccessService = sharedFileAccessService;
Logger = logger;
} }
// The following method/api endpoint needs some explanation: // The following method/api endpoint needs some explanation:
@@ -57,7 +59,7 @@ public class UploadController : Controller
if (path.Contains("..")) if (path.Contains(".."))
{ {
Logger.Warn("A path transversal attack has been detected while processing upload path", "security"); Logger.LogWarning("A path transversal attack has been detected while processing upload path: {path}", path);
return BadRequest("Invalid path. This attempt has been logged ;)"); return BadRequest("Invalid path. This attempt has been logged ;)");
} }

View File

@@ -1,6 +1,6 @@
using MoonCore.Blazor.Services;
using MoonCore.Exceptions; using MoonCore.Exceptions;
using MoonCore.Helpers; using MoonCore.Helpers;
using MoonCoreUI.Services;
using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Interfaces;
using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
@@ -23,9 +23,9 @@ public class ArchiveContextAction : IFileManagerContextAction
var alertService = provider.GetRequiredService<AlertService>(); var alertService = provider.GetRequiredService<AlertService>();
var fileName = await alertService.Text("Enter the archive file name", "", await alertService.Text("Create an archive", "Enter the archive file name",
Formatter.FormatDate(DateTime.UtcNow) + ".tar.gz"); async fileName =>
{
if (string.IsNullOrEmpty(fileName) || fileName.Contains("..")) // => canceled if (string.IsNullOrEmpty(fileName) || fileName.Contains("..")) // => canceled
return; return;
@@ -44,16 +44,18 @@ public class ArchiveContextAction : IFileManagerContextAction
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warn($"An error occured while archiving item ({entry.Name}):"); var logger = provider.GetRequiredService<ILogger<ArchiveContextAction>>();
Logger.Warn(e); logger.LogWarning("An error occured while archiving item ({name}): {e}", entry.Name, e);
await toastService.Danger("An unknown error occured while creating archive"); await toastService.Danger("An unknown error occured while creating archive");
} }
finally finally
{ {
await toastService.RemoveProgress("fileManagerArchive"); await toastService.DeleteProgress("fileManagerArchive");
} }
await fileManager.View.Refresh(); await fileManager.View.Refresh();
},
Formatter.FormatDate(DateTime.UtcNow) + ".tar.gz");
} }
} }

View File

@@ -1,6 +1,6 @@
using MoonCore.Blazor.Services;
using MoonCore.Exceptions; using MoonCore.Exceptions;
using MoonCore.Helpers; using MoonCore.Helpers;
using MoonCoreUI.Services;
using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Interfaces;
using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
@@ -21,9 +21,8 @@ public class ArchiveSelectionAction : IFileManagerSelectionAction
var alertService = provider.GetRequiredService<AlertService>(); var alertService = provider.GetRequiredService<AlertService>();
var fileName = await alertService.Text("Enter the archive file name", "", await alertService.Text("Create an archive", "Enter the archive file name", async fileName =>
Formatter.FormatDate(DateTime.UtcNow) + ".tar.gz"); {
if (string.IsNullOrEmpty(fileName) || fileName.Contains("..")) // => canceled if (string.IsNullOrEmpty(fileName) || fileName.Contains("..")) // => canceled
return; return;
@@ -42,14 +41,16 @@ public class ArchiveSelectionAction : IFileManagerSelectionAction
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warn($"An error occured while archiving items ({entries.Length}):"); var logger = provider.GetRequiredService<ILogger<ArchiveSelectionAction>>();
Logger.Warn(e); logger.LogWarning("An error occured while archiving items ({lenght}): {e}", entries.Length, e);
await toastService.Danger("An unknown error occured while creating archive"); await toastService.Danger("An unknown error occured while creating archive");
} }
finally finally
{ {
await toastService.RemoveProgress("fileManagerArchive"); await toastService.DeleteProgress("fileManagerArchive");
} }
},
Formatter.FormatDate(DateTime.UtcNow) + ".tar.gz");
} }
} }

View File

@@ -1,4 +1,4 @@
using MoonCoreUI.Services; using MoonCore.Blazor.Services;
using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Interfaces;
using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
@@ -14,8 +14,8 @@ public class CreateFileAction : IFileManagerCreateAction
{ {
var alertService = provider.GetRequiredService<AlertService>(); var alertService = provider.GetRequiredService<AlertService>();
var name = await alertService.Text("Enter a name for the new file"); await alertService.Text("Create a new file","Enter a name for the new file", async name =>
{
if (string.IsNullOrEmpty(name) || name.Contains("..")) if (string.IsNullOrEmpty(name) || name.Contains(".."))
return; return;
@@ -30,5 +30,6 @@ public class CreateFileAction : IFileManagerCreateAction
IsDirectory = false, IsDirectory = false,
LastModifiedAt = DateTime.UtcNow LastModifiedAt = DateTime.UtcNow
}); });
});
} }
} }

View File

@@ -1,4 +1,4 @@
using MoonCoreUI.Services; using MoonCore.Blazor.Services;
using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Interfaces;
using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
using Moonlight.Features.FileManager.UI.Components; using Moonlight.Features.FileManager.UI.Components;
@@ -16,8 +16,8 @@ public class CreateFolderAction : IFileManagerCreateAction
var alertService = provider.GetRequiredService<AlertService>(); var alertService = provider.GetRequiredService<AlertService>();
var toastService = provider.GetRequiredService<ToastService>(); var toastService = provider.GetRequiredService<ToastService>();
var name = await alertService.Text("Enter a name for the new directory"); await alertService.Text("Create a new folder", "Enter a name for the new directory", async name =>
{
if (string.IsNullOrEmpty(name) || name.Contains("..")) if (string.IsNullOrEmpty(name) || name.Contains(".."))
return; return;
@@ -25,5 +25,6 @@ public class CreateFolderAction : IFileManagerCreateAction
await toastService.Success("Successfully created directory"); await toastService.Success("Successfully created directory");
await fileManager.View.Refresh(); await fileManager.View.Refresh();
});
} }
} }

View File

@@ -1,4 +1,5 @@
using MoonCoreUI.Services;
using MoonCore.Blazor.Services;
using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Interfaces;
using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
using Moonlight.Features.FileManager.UI.Components; using Moonlight.Features.FileManager.UI.Components;

View File

@@ -1,4 +1,5 @@
using MoonCoreUI.Services;
using MoonCore.Blazor.Services;
using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Interfaces;
using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
using Moonlight.Features.FileManager.UI.Components; using Moonlight.Features.FileManager.UI.Components;
@@ -44,21 +45,22 @@ public class DeleteSelectionAction : IFileManagerSelectionAction
fileList += "And " + (fileCount - showFileCount) + " more files..."; fileList += "And " + (fileCount - showFileCount) + " more files...";
if(!await alertService.YesNo($"Do you really want to delete {folderCount + fileCount} item(s)? \n\n" + fileList)) await alertService.Confirm("Confirm file deletion",
return; $"Do you really want to delete {folderCount + fileCount} item(s)? \n\n" + fileList, async () =>
{
await toastService.CreateProgress("fileManagerSelectionDelete", "Deleting items"); await toastService.CreateProgress("fileManagerSelectionDelete", "Deleting items");
foreach (var entry in entries) foreach (var entry in entries)
{ {
await toastService.ModifyProgress("fileManagerSelectionDelete", $"Deleting '{entry.Name}'"); await toastService.UpdateProgress("fileManagerSelectionDelete", $"Deleting '{entry.Name}'");
await access.Delete(entry); await access.Delete(entry);
} }
await toastService.RemoveProgress("fileManagerSelectionDelete"); await toastService.DeleteProgress("fileManagerSelectionDelete");
await toastService.Success("Successfully deleted selection"); await toastService.Success("Successfully deleted selection");
await fileManager.View.Refresh(); await fileManager.View.Refresh();
});
} }
} }

View File

@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using MoonCore.Blazor.Services;
using MoonCore.Helpers; using MoonCore.Helpers;
using MoonCoreUI.Services;
using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Interfaces;
using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
using Moonlight.Features.FileManager.Services; using Moonlight.Features.FileManager.Services;
@@ -15,11 +16,11 @@ public class DownloadContextAction : IFileManagerContextAction
public string Color => "primary"; public string Color => "primary";
public Func<FileEntry, bool> Filter => entry => entry.IsFile; public Func<FileEntry, bool> Filter => entry => entry.IsFile;
public async Task Execute(BaseFileAccess access, UI.Components.FileManager fileManager, FileEntry entry, IServiceProvider serviceProvider) public async Task Execute(BaseFileAccess access, UI.Components.FileManager fileManager, FileEntry entry, IServiceProvider provider)
{ {
var fileAccessService = serviceProvider.GetRequiredService<SharedFileAccessService>(); var fileAccessService = provider.GetRequiredService<SharedFileAccessService>();
var navigation = serviceProvider.GetRequiredService<NavigationManager>(); var navigation = provider.GetRequiredService<NavigationManager>();
var toastService = serviceProvider.GetRequiredService<ToastService>(); var toastService = provider.GetRequiredService<ToastService>();
try try
{ {
@@ -31,8 +32,8 @@ public class DownloadContextAction : IFileManagerContextAction
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warn("Unable to start download"); var logger = provider.GetRequiredService<ILogger<DownloadContextAction>>();
Logger.Warn(e); logger.LogWarning("Unable to start download: {e}", e); ;
await toastService.Danger("Failed to start download"); await toastService.Danger("Failed to start download");
} }

View File

@@ -1,6 +1,7 @@
using MoonCore.Blazor.Services;
using MoonCore.Exceptions; using MoonCore.Exceptions;
using MoonCore.Helpers; using MoonCore.Helpers;
using MoonCoreUI.Services;
using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Interfaces;
using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
@@ -36,14 +37,14 @@ public class ExtractContextAction : IFileManagerContextAction
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warn($"An error occured while extracting archive ({entry.Name}):"); var logger = provider.GetRequiredService<ILogger<ExtractContextAction>>();
Logger.Warn(e); logger.LogWarning("An error occured while extracting archive ({name}): {e}", entry.Name, e);
await toastService.Danger("An unknown error occured while extracting archive"); await toastService.Danger("An unknown error occured while extracting archive");
} }
finally finally
{ {
await toastService.RemoveProgress("fileManagerExtract"); await toastService.DeleteProgress("fileManagerExtract");
} }
await fileManager.View.Refresh(); await fileManager.View.Refresh();

View File

@@ -1,4 +1,5 @@
using MoonCoreUI.Services;
using MoonCore.Blazor.Services;
using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Interfaces;
using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess;

View File

@@ -1,4 +1,5 @@
using MoonCoreUI.Services;
using MoonCore.Blazor.Services;
using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Interfaces;
using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
@@ -19,12 +20,12 @@ public class MoveSelectionAction : IFileManagerSelectionAction
foreach (var entry in entries) foreach (var entry in entries)
{ {
await toastService.ModifyProgress("fileManagerSelectionMove", $"Moving '{entry.Name}'"); await toastService.UpdateProgress("fileManagerSelectionMove", $"Moving '{entry.Name}'");
await access.Move(entry, path + entry.Name); await access.Move(entry, path + entry.Name);
} }
await toastService.RemoveProgress("fileManagerSelectionMove"); await toastService.DeleteProgress("fileManagerSelectionMove");
await toastService.Success("Successfully moved selection"); await toastService.Success("Successfully moved selection");
await fileManager.View.Refresh(); await fileManager.View.Refresh();

View File

@@ -1,4 +1,5 @@
using MoonCoreUI.Services;
using MoonCore.Blazor.Services;
using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Interfaces;
using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
using Moonlight.Features.FileManager.UI.Components; using Moonlight.Features.FileManager.UI.Components;
@@ -17,8 +18,8 @@ public class RenameContextAction : IFileManagerContextAction
var alertService = provider.GetRequiredService<AlertService>(); var alertService = provider.GetRequiredService<AlertService>();
var toastService = provider.GetRequiredService<ToastService>(); var toastService = provider.GetRequiredService<ToastService>();
var newName = await alertService.Text($"Enter a new name for '{entry.Name}'", "", entry.Name); await alertService.Text("Rename file" , $"Enter a new name for '{entry.Name}'", async newName =>
{
if (string.IsNullOrEmpty(newName)) if (string.IsNullOrEmpty(newName))
return; return;
@@ -26,5 +27,6 @@ public class RenameContextAction : IFileManagerContextAction
await fileManager.View.Refresh(); await fileManager.View.Refresh();
await toastService.Success("Successfully renamed file"); await toastService.Success("Successfully renamed file");
}, entry.Name);
} }
} }

View File

@@ -1,5 +1,6 @@
using Microsoft.JSInterop; using Microsoft.JSInterop;
using MoonCore.Attributes; using MoonCore.Attributes;
using MoonCore.Blazor.Extensions;
using MoonCore.Helpers; using MoonCore.Helpers;
namespace Moonlight.Features.FileManager.Services; namespace Moonlight.Features.FileManager.Services;
@@ -9,32 +10,30 @@ public class FileManagerInteropService
{ {
private readonly IJSRuntime JsRuntime; private readonly IJSRuntime JsRuntime;
public SmartEventHandler OnUploadStateChanged { get; set; } = new(); public SmartEventHandler OnUploadStateChanged { get; set; }
public FileManagerInteropService(IJSRuntime jsRuntime) public FileManagerInteropService(IJSRuntime jsRuntime, ILogger<SmartEventHandler> eventHandlerLogger)
{ {
JsRuntime = jsRuntime; JsRuntime = jsRuntime;
OnUploadStateChanged = new(eventHandlerLogger);
} }
public async Task InitDropzone(string id, string urlId) public async Task InitDropzone(string id, string urlId)
{ {
var reference = DotNetObjectReference.Create(this); var reference = DotNetObjectReference.Create(this);
await JsRuntime.InvokeVoidAsync("filemanager.dropzone.init", id, urlId, reference); await JsRuntime.InvokeVoidAsyncHandled("filemanager.dropzone.init", id, urlId, reference);
} }
public async Task InitFileSelect(string id, string urlId) public async Task InitFileSelect(string id, string urlId)
{ {
var reference = DotNetObjectReference.Create(this); var reference = DotNetObjectReference.Create(this);
await JsRuntime.InvokeVoidAsync("filemanager.fileselect.init", id, urlId, reference); await JsRuntime.InvokeVoidAsyncHandled("filemanager.fileselect.init", id, urlId, reference);
} }
public async Task UpdateUrl(string urlId, string url) public async Task UpdateUrl(string urlId, string url)
{ {
try await JsRuntime.InvokeVoidAsyncHandled("filemanager.updateUrl", urlId, url);
{
await JsRuntime.InvokeVoidAsync("filemanager.updateUrl", urlId, url);
}
catch (TaskCanceledException) { /* ignored */ }
} }
[JSInvokable] [JSInvokable]

View File

@@ -1,4 +1,3 @@
@using MoonCoreUI.Services
@using Moonlight.Core.Services @using Moonlight.Core.Services
@using MoonCore.Helpers @using MoonCore.Helpers
@using Moonlight.Core.Helpers @using Moonlight.Core.Helpers
@@ -7,6 +6,7 @@
@inject ToastService ToastService @inject ToastService ToastService
@inject HotKeyService HotKeyService @inject HotKeyService HotKeyService
@inject ILogger<FileEditor> Logger
@implements IDisposable @implements IDisposable
@@ -76,8 +76,7 @@
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warn($"An unhandled error has occured while saving a file using access type {FileAccess.GetType().FullName}"); Logger.LogWarning("An unhandled error has occured while saving a file using access type {name}: {e}", FileAccess.GetType().FullName, e);
Logger.Warn(e);
await ToastService.Danger("An unknown error has occured while saving the file. Please try again later"); await ToastService.Danger("An unknown error has occured while saving the file. Please try again later");
return; return;

View File

@@ -1,6 +1,5 @@
@using MoonCore.Helpers @using MoonCore.Helpers
@using MoonCore.Services @using MoonCore.Services
@using MoonCoreUI.Services
@using Moonlight.Core.Configuration @using Moonlight.Core.Configuration
@using Moonlight.Core.Services @using Moonlight.Core.Services
@using Moonlight.Features.FileManager.Interfaces @using Moonlight.Features.FileManager.Interfaces
@@ -9,6 +8,7 @@
@inject AlertService AlertService @inject AlertService AlertService
@inject ToastService ToastService @inject ToastService ToastService
@inject ModalService ModalService
@inject FileManagerInteropService FileManagerInteropService @inject FileManagerInteropService FileManagerInteropService
@inject SharedFileAccessService FileAccessService @inject SharedFileAccessService FileAccessService
@inject ConfigService<CoreConfiguration> ConfigService @inject ConfigService<CoreConfiguration> ConfigService
@@ -111,28 +111,6 @@ else
</ContextMenuTemplate> </ContextMenuTemplate>
</FileView> </FileView>
</div> </div>
<SmartModal @ref="FolderSelectModal" CssClasses="modal-lg modal-dialog-centered">
<div class="modal-header">
<h5 class="modal-title">@FolderSelectTitle</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="HideFolderSelect"></button>
</div>
<div class="modal-body">
<FileView @ref="FolderSelectView"
FileAccess="FolderSelectFileAccess"
Filter="FolderSelectFilter"
ShowDate="false"
ShowSelect="false"
ShowSize="false"
OnEntryClicked="EntryClickFolderSelect"
OnNavigateUpClicked="NavigateUpFolderSelect"
EnableContextMenu="false"/>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="HideFolderSelect">Cancel</button>
<button type="button" class="btn btn-primary" @onclick="SubmitFolderSelect">Submit</button>
</div>
</SmartModal>
} }
@code @code
@@ -151,15 +129,6 @@ else
private FileEntry FileToEdit; private FileEntry FileToEdit;
private bool ShowEditor = false; private bool ShowEditor = false;
// Folder select dialog
private bool FolderSelectIsOpen = false;
private SmartModal FolderSelectModal;
private BaseFileAccess FolderSelectFileAccess;
private string FolderSelectTitle;
private Func<string, Task> FolderSelectResult;
private FileView FolderSelectView;
private Func<FileEntry, bool> FolderSelectFilter => entry => entry.IsDirectory;
private Timer? UploadTokenTimer; private Timer? UploadTokenTimer;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
@@ -175,7 +144,6 @@ else
if (!firstRender) if (!firstRender)
return; return;
// Setup upload url update timer // Setup upload url update timer
UploadTokenTimer = new(async _ => UploadTokenTimer = new(async _ =>
{ {
@@ -308,52 +276,16 @@ else
#endregion #endregion
#region Selects
public async Task OpenFolderSelect(string title, Func<string, Task> onResult) public async Task OpenFolderSelect(string title, Func<string, Task> onResult)
{ {
if (FolderSelectIsOpen) await ModalService.Launch<FolderSelectModal>(cssClasses: "modal-lg modal-dialog-centered", buildAttributes: parameters =>
await HideFolderSelect();
FolderSelectResult = onResult;
FolderSelectTitle = title;
FolderSelectFileAccess = FileAccess.Clone();
await FolderSelectFileAccess.SetDirectory("/");
await FolderSelectModal.Show();
}
public async Task HideFolderSelect()
{ {
await FolderSelectModal.Hide(); parameters.Add("Title", title);
FolderSelectIsOpen = false; parameters.Add("OnResult", onResult);
FolderSelectFileAccess.Dispose(); parameters.Add("FileAccess", FileAccess.Clone());
});
} }
private async Task SubmitFolderSelect()
{
var path = await FolderSelectFileAccess.GetCurrentDirectory();
await HideFolderSelect();
await FolderSelectResult.Invoke(path);
}
private async Task NavigateUpFolderSelect()
{
await FolderSelectFileAccess.ChangeDirectory("..");
await FolderSelectView.Refresh();
}
private async Task EntryClickFolderSelect(FileEntry entry)
{
await FolderSelectFileAccess.ChangeDirectory(entry.Name);
await FolderSelectView.Refresh();
}
#endregion
public async void Dispose() public async void Dispose()
{ {
if (UploadTokenTimer != null) if (UploadTokenTimer != null)

View File

@@ -0,0 +1,61 @@
@using Moonlight.Features.FileManager.Models.Abstractions.FileAccess
@implements IDisposable
<div class="modal-header">
<h5 class="modal-title">@Title</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<FileView @ref="View"
FileAccess="FileAccess"
Filter="Filter"
ShowDate="false"
ShowSelect="false"
ShowSize="false"
OnEntryClicked="EntryClickFolderSelect"
OnNavigateUpClicked="NavigateUpFolderSelect"
EnableContextMenu="false"/>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" @onclick="SubmitFolderSelect">Submit</button>
</div>
@code
{
[Parameter] public BaseFileAccess FileAccess { get; set; }
[Parameter] public string Title { get; set; }
[Parameter] public Func<string, Task> OnResult { get; set; }
private FileView View;
private Func<FileEntry, bool> Filter => entry => entry.IsDirectory;
protected override async Task OnInitializedAsync()
{
await FileAccess.SetDirectory("/");
}
private async Task SubmitFolderSelect()
{
var path = await FileAccess.GetCurrentDirectory();
await OnResult.Invoke(path);
}
private async Task NavigateUpFolderSelect()
{
await FileAccess.ChangeDirectory("..");
await View.Refresh();
}
private async Task EntryClickFolderSelect(FileEntry entry)
{
await FileAccess.ChangeDirectory(entry.Name);
await View.Refresh();
}
public void Dispose()
{
FileAccess.Dispose();
}
}

View File

@@ -1,9 +1,16 @@
using MoonCore.Helpers; using MoonCore.Attributes;
using MoonCore.Helpers;
using Moonlight.Features.Servers.Entities; using Moonlight.Features.Servers.Entities;
namespace Moonlight.Features.Servers.Events; namespace Moonlight.Features.Servers.Events;
[Singleton]
public class ServerEvents public class ServerEvents
{ {
public static SmartEventHandler<(Server, ServerBackup)> OnBackupCompleted { get; set; } = new(); public SmartEventHandler<(Server, ServerBackup)> OnBackupCompleted { get; set; }
public ServerEvents(ILogger<SmartEventHandler> eventHandlerLogger)
{
OnBackupCompleted = new(eventHandlerLogger);
}
} }

View File

@@ -16,8 +16,13 @@ public static class NodeExtensions
return new HttpApiClient<NodeException>(remoteUrl, node.Token); return new HttpApiClient<NodeException>(remoteUrl, node.Token);
} }
public static JwtService<ServersJwtType> CreateJwtService(this ServerNode node) public static JwtService<ServersJwtType> CreateJwtService(this ServerNode node, ILoggerFactory factory)
{ {
return new JwtService<ServersJwtType>(node.Token); return node.CreateJwtService(factory.CreateLogger<JwtService<ServersJwtType>>());
}
public static JwtService<ServersJwtType> CreateJwtService(this ServerNode node, ILogger<JwtService<ServersJwtType>> logger)
{
return new JwtService<ServersJwtType>(node.Token, logger);
} }
} }

View File

@@ -8,10 +8,10 @@ namespace Moonlight.Features.Servers.Helpers;
public class ServerConsole : IDisposable public class ServerConsole : IDisposable
{ {
public SmartEventHandler<ServerState> OnStateChange { get; set; } = new(); public SmartEventHandler<ServerState> OnStateChange { get; set; }
public SmartEventHandler<ServerStats> OnStatsChange { get; set; } = new(); public SmartEventHandler<ServerStats> OnStatsChange { get; set; }
public SmartEventHandler<string> OnNewMessage { get; set; } = new(); public SmartEventHandler<string> OnNewMessage { get; set; }
public SmartEventHandler OnDisconnected { get; set; } = new(); public SmartEventHandler OnDisconnected { get; set; }
public ServerState State { get; private set; } = ServerState.Offline; public ServerState State { get; private set; } = ServerState.Offline;
public ServerStats Stats { get; private set; } = new(); public ServerStats Stats { get; private set; } = new();
@@ -20,18 +20,29 @@ public class ServerConsole : IDisposable
private readonly List<string> MessageCache = new(); private readonly List<string> MessageCache = new();
private readonly Server Server; private readonly Server Server;
private readonly ILogger<ServerConsole> Logger;
private readonly ILogger<AdvancedWebsocketStream> AwsLogger;
private ClientWebSocket WebSocket; private ClientWebSocket WebSocket;
private AdvancedWebsocketStream WebsocketStream; private AdvancedWebsocketStream WebsocketStream;
private CancellationTokenSource Cancellation = new(); private CancellationTokenSource Cancellation = new();
public ServerConsole(Server server) public ServerConsole(Server server, ILoggerFactory loggerFactory)
{ {
if (server.Node == null) if (server.Node == null)
throw new ArgumentNullException(nameof(server.Node)); throw new ArgumentNullException(nameof(server.Node));
Server = server; Server = server;
Logger = loggerFactory.CreateLogger<ServerConsole>();
AwsLogger = loggerFactory.CreateLogger<AdvancedWebsocketStream>();
var eventHandlerLogger = loggerFactory.CreateLogger<SmartEventHandler>();
OnStateChange = new(eventHandlerLogger);
OnStatsChange = new(eventHandlerLogger);
OnDisconnected = new(eventHandlerLogger);
OnNewMessage = new(eventHandlerLogger);
} }
public async Task Connect() public async Task Connect()
@@ -49,7 +60,7 @@ public class ServerConsole : IDisposable
wsUrl = $"ws://{Server.Node.Fqdn}:{Server.Node.HttpPort}/servers/{Server.Id}/ws"; wsUrl = $"ws://{Server.Node.Fqdn}:{Server.Node.HttpPort}/servers/{Server.Id}/ws";
await WebSocket.ConnectAsync(new Uri(wsUrl), CancellationToken.None); await WebSocket.ConnectAsync(new Uri(wsUrl), CancellationToken.None);
WebsocketStream = new AdvancedWebsocketStream(WebSocket); WebsocketStream = new AdvancedWebsocketStream(AwsLogger, WebSocket);
WebsocketStream.RegisterPacket<string>(1); WebsocketStream.RegisterPacket<string>(1);
WebsocketStream.RegisterPacket<ServerState>(2); WebsocketStream.RegisterPacket<ServerState>(2);
@@ -103,11 +114,10 @@ public class ServerConsole : IDisposable
break; break;
if (e is WebSocketException) if (e is WebSocketException)
Logger.Warn($"Lost connection to daemon server websocket: {e.Message}"); Logger.LogWarning("Lost connection to daemon server websocket: {message}", e.Message);
else else
{ {
Logger.Warn("Server console ws disconnected because of application error:"); Logger.LogWarning("Server console ws disconnected because of application error: {e}", e);
Logger.Warn(e);
} }
break; break;

View File

@@ -18,11 +18,17 @@ public class ServersController : Controller
{ {
private readonly Repository<Server> ServerRepository; private readonly Repository<Server> ServerRepository;
private readonly Repository<ServerBackup> BackupRepository; private readonly Repository<ServerBackup> BackupRepository;
private readonly ILogger<ServersController> Logger;
private readonly ILogger<AdvancedWebsocketStream> WebSocketLogger;
private readonly ServerEvents ServerEvents;
public ServersController(Repository<Server> serverRepository, Repository<ServerBackup> backupRepository) public ServersController(Repository<Server> serverRepository, Repository<ServerBackup> backupRepository, ILogger<ServersController> logger, ILogger<AdvancedWebsocketStream> webSocketLogger, ServerEvents serverEvents)
{ {
ServerRepository = serverRepository; ServerRepository = serverRepository;
BackupRepository = backupRepository; BackupRepository = backupRepository;
Logger = logger;
WebSocketLogger = webSocketLogger;
ServerEvents = serverEvents;
} }
[HttpGet("ws")] [HttpGet("ws")]
@@ -36,7 +42,7 @@ public class ServersController : Controller
var websocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); var websocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
// Build connection wrapper // Build connection wrapper
var websocketStream = new AdvancedWebsocketStream(websocket); var websocketStream = new AdvancedWebsocketStream(WebSocketLogger, websocket);
websocketStream.RegisterPacket<int>(1); websocketStream.RegisterPacket<int>(1);
websocketStream.RegisterPacket<ServerConfiguration>(2); websocketStream.RegisterPacket<ServerConfiguration>(2);
@@ -66,8 +72,7 @@ public class ServersController : Controller
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Error($"An error occured while sending server {server.Id} (Image: {server.Image.Name}) to daemon. This may indicate a corrupt or broken image/server. Skipping this server"); Logger.LogError("An error occured while sending server {serverId} (Image: {name}) to daemon. This may indicate a corrupt or broken image/server. Skipping this server. Error: {e}", server.Id, server.Image.Name, e);
Logger.Error(e);
} }
} }
@@ -146,7 +151,7 @@ public class ServersController : Controller
return NotFound(); return NotFound();
if(!status.Successful) if(!status.Successful)
Logger.Warn($"A node reported an error for a backup for the server {server.Id}"); Logger.LogWarning("A node reported an error for a backup for the server {serverId}", server.Id);
backup.Successful = status.Successful; backup.Successful = status.Successful;
backup.Completed = true; backup.Completed = true;

View File

@@ -1,9 +1,9 @@
using MoonCoreUI.Helpers; using MoonCore.Blazor.Helpers;
using Moonlight.Core.Interfaces.Ui.Admin; using Moonlight.Core.Interfaces.Ui.Admin;
using Moonlight.Core.Models.Abstractions; using Moonlight.Core.Models.Abstractions;
using Moonlight.Features.Servers.UI.Components.Cards; using Moonlight.Features.Servers.UI.Components.Cards;
namespace Moonlight.Features.Servers.Implementations.UI.Admin.AdminColumns; namespace Moonlight.Features.Servers.Implementations.AdminDashboard.Columns;
public class ServerCount : IAdminDashboardColumn public class ServerCount : IAdminDashboardColumn
{ {

View File

@@ -1,9 +1,9 @@
using MoonCoreUI.Helpers; using MoonCore.Blazor.Helpers;
using Moonlight.Core.Interfaces.Ui.Admin; using Moonlight.Core.Interfaces.Ui.Admin;
using Moonlight.Core.Models.Abstractions; using Moonlight.Core.Models.Abstractions;
using Moonlight.Features.Servers.UI.Components.Cards; using Moonlight.Features.Servers.UI.Components.Cards;
namespace Moonlight.Features.Servers.Implementations.UI.Admin.AdminComponents; namespace Moonlight.Features.Servers.Implementations.AdminDashboard.Components;
public class NodeOverview : IAdminDashboardComponent public class NodeOverview : IAdminDashboardComponent
{ {

View File

@@ -1,9 +1,8 @@
using MoonCoreUI.Helpers; using MoonCore.Blazor.Helpers;
using Moonlight.Core.Interfaces.UI.User; using Moonlight.Core.Interfaces.UI.User;
using Moonlight.Core.Models.Abstractions; using Moonlight.Core.Models.Abstractions;
using Moonlight.Features.Servers.UI.Components.Cards;
namespace Moonlight.Features.Servers.Implementations.UI.UserDashboard.Components; namespace Moonlight.Features.Servers.Implementations.UserDashboard.Components;
public class UserDashboardServerCount : IUserDashboardComponent public class UserDashboardServerCount : IUserDashboardComponent
{ {

View File

@@ -1,6 +1,6 @@
using System.ComponentModel; using System.ComponentModel;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using MoonCoreUI.Attributes; using MoonCore.Blazor.Attributes.Auto;
using Moonlight.Core.Database.Entities; using Moonlight.Core.Database.Entities;
using Moonlight.Features.Servers.Entities; using Moonlight.Features.Servers.Entities;
@@ -12,11 +12,11 @@ public class CreateServerForm
public string Name { get; set; } public string Name { get; set; }
[Required(ErrorMessage = "You need to specify a server owner")] [Required(ErrorMessage = "You need to specify a server owner")]
[Selector(SelectorProp = "Username", DisplayProp = "Username", UseDropdown = true)] //[Selector(SelectorProp = "Username", DisplayProp = "Username", UseDropdown = true)]
public User Owner { get; set; } public User Owner { get; set; }
[Required(ErrorMessage = "You need to specify a server image")] [Required(ErrorMessage = "You need to specify a server image")]
[Selector(SelectorProp = "Name", DisplayProp = "Name", UseDropdown = true)] //[Selector(SelectorProp = "Name", DisplayProp = "Name", UseDropdown = true)]
public ServerImage Image { get; set; } public ServerImage Image { get; set; }
[Range(1, int.MaxValue, ErrorMessage = "Enter a valid cpu value")] [Range(1, int.MaxValue, ErrorMessage = "Enter a valid cpu value")]
@@ -26,31 +26,31 @@ public class CreateServerForm
[Range(1, int.MaxValue, ErrorMessage = "Enter a valid memory value")] [Range(1, int.MaxValue, ErrorMessage = "Enter a valid memory value")]
[Description("The amount of memory this server will be able to use")] [Description("The amount of memory this server will be able to use")]
[ByteSize(MinimumUnit = 1, Converter = 1, DefaultUnit = 2)] //[ByteSize(MinimumUnit = 1, Converter = 1, DefaultUnit = 2)]
[Section("Resources", Icon = "bxs-chip")] [Section("Resources", Icon = "bxs-chip")]
public int Memory { get; set; } public int Memory { get; set; }
[Range(1, int.MaxValue, ErrorMessage = "Enter a valid disk value")] [Range(1, int.MaxValue, ErrorMessage = "Enter a valid disk value")]
[Description("The amount of disk space this server will be able to use")] [Description("The amount of disk space this server will be able to use")]
[ByteSize(MinimumUnit = 1, Converter = 1, DefaultUnit = 2)] //[ByteSize(MinimumUnit = 1, Converter = 1, DefaultUnit = 2)]
[Section("Resources", Icon = "bxs-chip")] [Section("Resources", Icon = "bxs-chip")]
public int Disk { get; set; } public int Disk { get; set; }
[Description("Whether to use a virtual disk for storing server files. Dont use this if you want to overallocate as the virtual disks will fill out the space you allocate")] [Description("Whether to use a virtual disk for storing server files. Dont use this if you want to overallocate as the virtual disks will fill out the space you allocate")]
[Section("Deployment", Icon = "bx-cube")] [Section("Deployment", Icon = "bx-cube")]
[RadioButtonBool("Virtual Disk", "Simple Volume", TrueIcon = "bxs-hdd", FalseIcon = "bxs-data")] //[RadioButtonBool("Virtual Disk", "Simple Volume", TrueIcon = "bxs-hdd", FalseIcon = "bxs-data")]
[DisplayName("Storage")] [DisplayName("Storage")]
public bool UseVirtualDisk { get; set; } public bool UseVirtualDisk { get; set; }
[Required(ErrorMessage = "You need to specify a server node")] [Required(ErrorMessage = "You need to specify a server node")]
[Selector(SelectorProp = "Name", DisplayProp = "Name", UseDropdown = true)] //[Selector(SelectorProp = "Name", DisplayProp = "Name", UseDropdown = true)]
[Section("Deployment", Icon = "bx-cube")] [Section("Deployment", Icon = "bx-cube")]
public ServerNode Node { get; set; } public ServerNode Node { get; set; }
[Description("The allocations the server should have")] [Description("The allocations the server should have")]
[MultiSelection("Port", "Port", Icon = "bx-network-chart")] //TODO: [MultiSelection("Port", "Port", Icon = "bx-network-chart")]
[Section("Deployment", Icon = "bx-cube")] [Section("Deployment", Icon = "bx-cube")]
[CustomItemLoader("FreeAllocations")] //[CustomItemLoader("FreeAllocations")]
[CustomDisplayFunction("AllocationWithIp")] //[CustomDisplayFunction("AllocationWithIp")]
public List<ServerAllocation> Allocations { get; set; } = new(); public List<ServerAllocation> Allocations { get; set; } = new();
} }

View File

@@ -1,5 +1,4 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using MoonCoreUI.Attributes;
using Moonlight.Features.Servers.Entities; using Moonlight.Features.Servers.Entities;
namespace Moonlight.Features.Servers.Models.Forms.Users.Networks; namespace Moonlight.Features.Servers.Models.Forms.Users.Networks;
@@ -10,6 +9,6 @@ public class CreateNetworkForm
public string Name { get; set; } public string Name { get; set; }
[Required(ErrorMessage = "You need to specify a node to create the network on")] [Required(ErrorMessage = "You need to specify a node to create the network on")]
[Selector(SelectorProp = "Name", DisplayProp = "Name")] //[Selector(SelectorProp = "Name", DisplayProp = "Name")]
public ServerNode Node { get; set; } public ServerNode Node { get; set; }
} }

View File

@@ -9,13 +9,13 @@ using Moonlight.Core.Services;
using Moonlight.Features.Servers.Actions; using Moonlight.Features.Servers.Actions;
using Moonlight.Features.Servers.Configuration; using Moonlight.Features.Servers.Configuration;
using Moonlight.Features.Servers.Http.Middleware; using Moonlight.Features.Servers.Http.Middleware;
using Moonlight.Features.Servers.Implementations.AdminDashboard.Columns;
using Moonlight.Features.Servers.Implementations.AdminDashboard.Components;
using Moonlight.Features.Servers.Implementations.Diagnose; using Moonlight.Features.Servers.Implementations.Diagnose;
using Moonlight.Features.Servers.Implementations.UI.Admin.AdminColumns;
using Moonlight.Features.Servers.Implementations.UI.Admin.AdminComponents;
using Moonlight.Features.Servers.Models.Enums; using Moonlight.Features.Servers.Models.Enums;
using Moonlight.Features.Servers.Services; using Moonlight.Features.Servers.Services;
using Moonlight.Features.Servers.UI.Components.Cards; using Moonlight.Features.Servers.UI.Components.Cards;
using UserDashboardServerCount = Moonlight.Features.Servers.Implementations.UI.UserDashboard.Components.UserDashboardServerCount; using UserDashboardServerCount = Moonlight.Features.Servers.Implementations.UserDashboard.Components.UserDashboardServerCount;
namespace Moonlight.Features.Servers; namespace Moonlight.Features.Servers;
@@ -34,7 +34,7 @@ public class ServersFeature : MoonlightFeature
// //
var config = new ConfigService<CoreConfiguration>(PathBuilder.File("storage", "configs", "core.json")); var config = new ConfigService<CoreConfiguration>(PathBuilder.File("storage", "configs", "core.json"));
context.Builder.Services.AddSingleton(new JwtService<ServersJwtType>(config.Get().Security.Token)); context.Builder.Services.AddSingleton(new JwtService<ServersJwtType>(config.Get().Security.Token, context.LoggerFactory.CreateLogger<JwtService<ServersJwtType>>()));
// //
var configService = new ConfigService<ServersConfiguration>(PathBuilder.File("storage", "configs", "servers.json")); var configService = new ConfigService<ServersConfiguration>(PathBuilder.File("storage", "configs", "servers.json"));

View File

@@ -11,10 +11,12 @@ namespace Moonlight.Features.Servers.Services;
public class NodeService public class NodeService
{ {
private readonly IServiceProvider ServiceProvider; private readonly IServiceProvider ServiceProvider;
private readonly ILogger<NodeService> Logger;
public NodeService(IServiceProvider serviceProvider) public NodeService(IServiceProvider serviceProvider, ILogger<NodeService> logger)
{ {
ServiceProvider = serviceProvider; ServiceProvider = serviceProvider;
Logger = logger;
} }
public async Task Boot(ServerNode node) public async Task Boot(ServerNode node)
@@ -42,8 +44,7 @@ public class NodeService
{ {
//TODO: Add http exception check to reduce error logs //TODO: Add http exception check to reduce error logs
Logger.Warn($"An error occured while booting node '{node.Name}'"); Logger.LogWarning("An error occured while booting node '{name}': {e}", node.Name, e);
Logger.Warn(e);
} }
}); });
} }

View File

@@ -116,7 +116,8 @@ public class ServerBackupService
var remoteUrl = $"{protocol}://{node.Fqdn}:{node.HttpPort}/"; var remoteUrl = $"{protocol}://{node.Fqdn}:{node.HttpPort}/";
// Build jwt // Build jwt
var jwtService = node.CreateJwtService(); var loggerFactory = ServiceProvider.GetRequiredService<ILoggerFactory>();
var jwtService = node.CreateJwtService(loggerFactory);
var jwt = await jwtService.Create(data => var jwt = await jwtService.Create(data =>
{ {

View File

@@ -14,10 +14,12 @@ public class ServerScheduleService
{ {
private readonly IServiceProvider ServiceProvider; private readonly IServiceProvider ServiceProvider;
public readonly Dictionary<string, ScheduleAction> Actions = new(); public readonly Dictionary<string, ScheduleAction> Actions = new();
private readonly ILogger<ServerScheduleService> Logger;
public ServerScheduleService(IServiceProvider serviceProvider) public ServerScheduleService(IServiceProvider serviceProvider, ILogger<ServerScheduleService> logger)
{ {
ServiceProvider = serviceProvider; ServiceProvider = serviceProvider;
Logger = logger;
} }
public Task RegisterAction<T>(string id) where T : ScheduleAction public Task RegisterAction<T>(string id) where T : ScheduleAction
@@ -50,7 +52,7 @@ public class ServerScheduleService
{ {
if (!Actions.ContainsKey(scheduleItem.Action)) if (!Actions.ContainsKey(scheduleItem.Action))
{ {
Logger.Warn($"The server {server.Id} has a invalid action type '{scheduleItem.Action}'"); Logger.LogWarning("The server {serverId} has a invalid action type '{action}'", server.Id, scheduleItem.Action);
continue; continue;
} }
@@ -69,8 +71,7 @@ public class ServerScheduleService
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warn($"An unhandled error occured while running schedule {schedule.Name} for server {server.Id}"); Logger.LogWarning("An unhandled error occured while running schedule {name} for server {serverId}: {e}", schedule.Name, server.Id, e);
Logger.Warn(e);
sw.Stop(); sw.Stop();

View File

@@ -27,10 +27,12 @@ public class ServerService
public NodeService NodeService => ServiceProvider.GetRequiredService<NodeService>(); public NodeService NodeService => ServiceProvider.GetRequiredService<NodeService>();
private readonly IServiceProvider ServiceProvider; private readonly IServiceProvider ServiceProvider;
private readonly ILogger<ServerService> Logger;
public ServerService(IServiceProvider serviceProvider) public ServerService(IServiceProvider serviceProvider, ILogger<ServerService> logger)
{ {
ServiceProvider = serviceProvider; ServiceProvider = serviceProvider;
Logger = logger;
} }
public async Task Sync(Server server) public async Task Sync(Server server)
@@ -84,8 +86,7 @@ public class ServerService
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warn($"Could not establish to the node with the id {node.Id}"); Logger.LogWarning("Could not establish to the node with the id {nodeId}: {e}", node.Id, e);
Logger.Warn(e);
throw new DisplayException($"Could not establish connection to the node: {e.Message}"); throw new DisplayException($"Could not establish connection to the node: {e.Message}");
} }

View File

@@ -1,6 +1,6 @@
@using XtermBlazor @using XtermBlazor
@using MoonCoreUI.Services
@inject ClipboardService ClipboardService @inject ClipboardService ClipboardService
@inject ToastService ToastService @inject ToastService ToastService

View File

@@ -1,6 +1,5 @@
@using Moonlight.Features.Servers.Entities @using Moonlight.Features.Servers.Entities
@using MoonCore.Abstractions @using MoonCore.Abstractions
@using MoonCoreUI.Services
@inject Repository<ServerVariable> ServerVariableRepository @inject Repository<ServerVariable> ServerVariableRepository
@inject ToastService ToastService @inject ToastService ToastService

View File

@@ -1,6 +1,6 @@
@using Moonlight.Features.Servers.Entities @using Moonlight.Features.Servers.Entities
@using MoonCore.Abstractions @using MoonCore.Abstractions
@using MoonCoreUI.Services
@using System.Text.RegularExpressions @using System.Text.RegularExpressions
@using MoonCore.Helpers @using MoonCore.Helpers

View File

@@ -1,6 +1,6 @@
@using Moonlight.Features.Servers.Entities @using Moonlight.Features.Servers.Entities
@using MoonCore.Abstractions @using MoonCore.Abstractions
@using MoonCoreUI.Services
@using System.Text.RegularExpressions @using System.Text.RegularExpressions
@inject Repository<ServerVariable> ServerVariableRepository @inject Repository<ServerVariable> ServerVariableRepository

View File

@@ -1,6 +1,6 @@
@using Moonlight.Features.Servers.Entities @using Moonlight.Features.Servers.Entities
@using MoonCore.Abstractions @using MoonCore.Abstractions
@using MoonCoreUI.Services
@inject Repository<ServerVariable> ServerVariableRepository @inject Repository<ServerVariable> ServerVariableRepository
@inject ToastService ToastService @inject ToastService ToastService

View File

@@ -1,8 +1,9 @@
@using Moonlight.Features.Servers.Models.Forms.Admin.Images @using Moonlight.Features.Servers.Models.Forms.Admin.Images
<div class="mt-5 card card-body p-8"> <div class="mt-5 card card-body p-8">
<div class="row g-5"> <div class="row g-5">@*
<div class="col-md-6 col-12"> <div class="col-md-6 col-12">
<input class="form-control"/>
<SingleAutoProperty TItem="UpdateImageDetailedForm" <SingleAutoProperty TItem="UpdateImageDetailedForm"
Model="Form" Model="Form"
Field="@(x => x.Name)"/> Field="@(x => x.Name)"/>
@@ -21,7 +22,7 @@
<SingleAutoProperty TItem="UpdateImageDetailedForm" <SingleAutoProperty TItem="UpdateImageDetailedForm"
Model="Form" Model="Form"
Field="@(x => x.UpdateUrl)"/> Field="@(x => x.UpdateUrl)"/>
</div> </div>*@
</div> </div>
</div> </div>

View File

@@ -8,7 +8,7 @@
<div class="row g-8"> <div class="row g-8">
<div class="col-md-6 col-12"> <div class="col-md-6 col-12">
<label class="form-label">Default docker image</label> <label class="form-label">Default docker image</label>
<SmartSelect TField="ServerDockerImage" <MCBSelect TField="ServerDockerImage"
@bind-Value="SelectedDockerImage" @bind-Value="SelectedDockerImage"
Items="Image.DockerImages" Items="Image.DockerImages"
DisplayField="@(x => x.Name)" DisplayField="@(x => x.Name)"
@@ -23,7 +23,7 @@
</div> </div>
</div> </div>
</div> </div>
@*
<AutoListCrud TItem="ServerDockerImage" <AutoListCrud TItem="ServerDockerImage"
TRootItem="ServerImage" TRootItem="ServerImage"
TCreateForm="CreateDockerImage" TCreateForm="CreateDockerImage"
@@ -53,7 +53,7 @@
</IconAlert> </IconAlert>
</NoItemsView> </NoItemsView>
</AutoListCrud> </AutoListCrud>
*@
@code @code
{ {
[Parameter] public ServerImage Image { get; set; } [Parameter] public ServerImage Image { get; set; }

View File

@@ -2,7 +2,7 @@
@using Moonlight.Features.FileManager.UI.Components @using Moonlight.Features.FileManager.UI.Components
<div class="mt-5 card card-body p-8"> <div class="mt-5 card card-body p-8">
<div class="row g-5"> <div class="row g-5">@*
<div class="col-md-6 col-12"> <div class="col-md-6 col-12">
<SingleAutoProperty TItem="UpdateImageDetailedForm" <SingleAutoProperty TItem="UpdateImageDetailedForm"
Model="Form" Model="Form"
@@ -17,7 +17,7 @@
<SingleAutoProperty TItem="UpdateImageDetailedForm" <SingleAutoProperty TItem="UpdateImageDetailedForm"
Model="Form" Model="Form"
Field="@(x => x.AllocationsNeeded)"/> Field="@(x => x.AllocationsNeeded)"/>
</div> </div>*@
<div class="col-12"> <div class="col-12">
<Editor InitialContent="@Form.InstallScript" <Editor InitialContent="@Form.InstallScript"
Mode="sh" Mode="sh"

View File

@@ -1,7 +1,7 @@
@using Moonlight.Features.Servers.Models.Forms.Admin.Images @using Moonlight.Features.Servers.Models.Forms.Admin.Images
<div class="mt-5 card card-body p-8"> <div class="mt-5 card card-body p-8">
<div class="row g-5"> <div class="row g-5">@*
<div class="col-md-6 col-12"> <div class="col-md-6 col-12">
<SingleAutoProperty TItem="UpdateImageDetailedForm" <SingleAutoProperty TItem="UpdateImageDetailedForm"
Model="Form" Model="Form"
@@ -16,7 +16,7 @@
<SingleAutoProperty TItem="UpdateImageDetailedForm" <SingleAutoProperty TItem="UpdateImageDetailedForm"
Model="Form" Model="Form"
Field="@(x => x.OnlineDetection)"/> Field="@(x => x.OnlineDetection)"/>
</div> </div>*@
</div> </div>
</div> </div>

View File

@@ -1,7 +1,7 @@
@using Moonlight.Features.Servers.Entities @using Moonlight.Features.Servers.Entities
@using Moonlight.Features.Servers.Models.Forms.Admin.Images.Variables @using Moonlight.Features.Servers.Models.Forms.Admin.Images.Variables
@using BlazorTable @using BlazorTable
@*
<AutoListCrud TItem="ServerImageVariable" <AutoListCrud TItem="ServerImageVariable"
TRootItem="ServerImage" TRootItem="ServerImage"
TCreateForm="CreateImageVariable" TCreateForm="CreateImageVariable"
@@ -45,7 +45,7 @@
</IconAlert> </IconAlert>
</NoItemsView> </NoItemsView>
</AutoListCrud> </AutoListCrud>
*@
@code @code
{ {
[Parameter] public ServerImage Image { get; set; } [Parameter] public ServerImage Image { get; set; }

View File

@@ -4,7 +4,6 @@
@using Moonlight.Features.Servers.UI.Components @using Moonlight.Features.Servers.UI.Components
@using MoonCore.Abstractions @using MoonCore.Abstractions
@using MoonCore.Helpers @using MoonCore.Helpers
@using MoonCoreUI.Services
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@using Moonlight.Features.Servers.Helpers @using Moonlight.Features.Servers.Helpers
@using Moonlight.Features.Servers.UI.UserViews @using Moonlight.Features.Servers.UI.UserViews
@@ -13,7 +12,8 @@
@using MoonCore.Exceptions @using MoonCore.Exceptions
@using Moonlight.Features.Servers.Configuration @using Moonlight.Features.Servers.Configuration
@using MoonCore.Services @using MoonCore.Services
@using Moonlight.Core.Services @using IdentityService = Moonlight.Core.Services.IdentityService
@using MoonCore.Blazor.Forms.Router
@inject Repository<Server> ServerRepository @inject Repository<Server> ServerRepository
@inject ServerService ServerService @inject ServerService ServerService
@@ -21,10 +21,12 @@
@inject AlertService AlertService @inject AlertService AlertService
@inject IdentityService IdentityService @inject IdentityService IdentityService
@inject ConfigService<ServersConfiguration> ConfigService @inject ConfigService<ServersConfiguration> ConfigService
@inject ILogger<UserLayout> Logger
@inject ILoggerFactory LoggerFactory
@implements IDisposable @implements IDisposable
<LazyLoader Load="Load" ShowAsCard="true"> <LazyLoader Load="Load">
<div class="card card-body pb-5 pt-5"> <div class="card card-body pb-5 pt-5">
<div> <div>
<div class="row px-2"> <div class="row px-2">
@@ -154,7 +156,7 @@
{ {
<CascadingValue Value="Server"> <CascadingValue Value="Server">
<CascadingValue Value="Console"> <CascadingValue Value="Console">
<SmartRouter Route="@Route"> <MCBRouter Route="@Route">
<Route Path="/"> <Route Path="/">
<Console/> <Console/>
</Route> </Route>
@@ -186,7 +188,7 @@
<Route Path="/reset"> <Route Path="/reset">
<Reset/> <Reset/>
</Route> </Route>
</SmartRouter> </MCBRouter>
</CascadingValue> </CascadingValue>
</CascadingValue> </CascadingValue>
} }
@@ -233,7 +235,7 @@
await lazyLoader.SetText("Establishing a connection to the server"); await lazyLoader.SetText("Establishing a connection to the server");
// Create console wrapper // Create console wrapper
Console = new ServerConsole(Server); Console = new ServerConsole(Server, LoggerFactory);
// Configure // Configure
Console.OnStateChange += async state => await HandleStateChange(state); Console.OnStateChange += async state => await HandleStateChange(state);
@@ -266,7 +268,7 @@
if (httpRequestException.InnerException is not SocketException socketException) if (httpRequestException.InnerException is not SocketException socketException)
throw; throw;
Logger.Warn($"Unable to access the node's websocket endpoint: {socketException.Message}"); Logger.LogWarning("Unable to access the node's websocket endpoint: {socketException}", socketException);
// Change the ui and... // Change the ui and...
IsNodeOffline = true; IsNodeOffline = true;
@@ -328,7 +330,12 @@
{ {
if (!ConfigService.Get().DisableServerKillWarning) if (!ConfigService.Get().DisableServerKillWarning)
{ {
if (!await AlertService.YesNo("Do you really want to kill the server? This can result in data loss or corrupted server files")) await AlertService.Confirm(
"Server kill confirmation",
"Do you really want to kill the server? This can result in data loss or corrupted server files",
async () => await SendSignalHandled(PowerAction.Kill)
);
return; return;
} }
@@ -347,8 +354,7 @@
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warn($"An error occured while sending power action {action} to server {Server.Id}:"); Logger.LogWarning("An error occured while sending power action {action} to server {serverId}: {e}", action, Server.Id, e);
Logger.Warn(e);
await ToastService.Danger("An error occured while sending power action to server. Check the console for more information"); await ToastService.Danger("An error occured while sending power action to server. Check the console for more information");
} }

View File

@@ -3,7 +3,7 @@
@using BlazorTable @using BlazorTable
@using MoonCore.Abstractions @using MoonCore.Abstractions
@using MoonCore.Exceptions @using MoonCore.Exceptions
@using MoonCoreUI.Services
@inject Repository<ServerNode> NodeRepository @inject Repository<ServerNode> NodeRepository
@inject Repository<ServerAllocation> AllocationRepository @inject Repository<ServerAllocation> AllocationRepository
@@ -32,7 +32,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-8 col-12"> <div class="col-md-8 col-12">@*
<AutoListCrud TRootItem="ServerNode" <AutoListCrud TRootItem="ServerNode"
TItem="ServerAllocation" TItem="ServerAllocation"
TCreateForm="CreateAllocationForm" TCreateForm="CreateAllocationForm"
@@ -58,7 +58,7 @@
In order for a server to be deployed on this node allocations need to be created here In order for a server to be deployed on this node allocations need to be created here
</IconAlert> </IconAlert>
</NoItemsView> </NoItemsView>
</AutoListCrud> </AutoListCrud>*@
</div> </div>
</div> </div>
@@ -67,7 +67,7 @@
[Parameter] public ServerNode Node { get; set; } [Parameter] public ServerNode Node { get; set; }
// A bit long, lol // A bit long, lol
private AutoListCrud<ServerAllocation, ServerNode, CreateAllocationForm, UpdateAllocationForm> Crud; //private AutoListCrud<ServerAllocation, ServerNode, CreateAllocationForm, UpdateAllocationForm> Crud;
// Quick add values // Quick add values
private string IpAddress = "0.0.0.0"; private string IpAddress = "0.0.0.0";
@@ -101,14 +101,13 @@
NodeRepository.Update(Node!); NodeRepository.Update(Node!);
await ToastService.Success($"Added {added} allocations and skipped {skipped} ports due to existing allocations"); await ToastService.Success($"Added {added} allocations and skipped {skipped} ports due to existing allocations");
await Crud.Reload(); //await Crud.Reload();
} }
private async Task DeleteAllAllocations() private async Task DeleteAllAllocations()
{ {
if (!await AlertService.YesNo("Do you really want to delete all allocations?", "Yes", "No")) await AlertService.Confirm("Confirm mass deletion", "Do you really want to delete all allocations?", async () =>
return; {
foreach (var allocation in Node!.Allocations.ToArray()) // To array in order to prevent collection modified exception foreach (var allocation in Node!.Allocations.ToArray()) // To array in order to prevent collection modified exception
{ {
// Check if a server is using this allocation before deleting // Check if a server is using this allocation before deleting
@@ -125,7 +124,8 @@
} }
await ToastService.Success("Successfully deleted allocations"); await ToastService.Success("Successfully deleted allocations");
await Crud.Reload(); //await Crud.Reload();
});
} }
private Task ValidateDelete(ServerAllocation allocation) private Task ValidateDelete(ServerAllocation allocation)

View File

@@ -1,13 +1,14 @@
@using MoonCore.Helpers @using MoonCore.Helpers
@using Moonlight.Features.Servers.Entities @using Moonlight.Features.Servers.Entities
@using MoonCore.Services @using MoonCore.Services
@using MoonCoreUI.Services
@using Moonlight.Core.Configuration @using Moonlight.Core.Configuration
@using Moonlight.Features.Servers.Services @using Moonlight.Features.Servers.Services
@inject ConfigService<CoreConfiguration> ConfigService @inject ConfigService<CoreConfiguration> ConfigService
@inject NodeService NodeService @inject NodeService NodeService
@inject ToastService ToastService @inject ToastService ToastService
@inject ILogger<NodeLogs> Logger
<div class="card card-body mb-5 p-3"> <div class="card card-body mb-5 p-3">
<div class="d-flex justify-content-between align-items-center"> <div class="d-flex justify-content-between align-items-center">
@@ -69,8 +70,7 @@
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warn($"An error occured while fetching logs from node '{Node.Name}'"); Logger.LogWarning("An error occured while fetching logs from node '{name}': {e}", Node.Name, e);
Logger.Warn(e);
await ToastService.Danger("An error occured while fetching logs. Please try again later"); await ToastService.Danger("An error occured while fetching logs. Please try again later");
} }

View File

@@ -2,6 +2,7 @@
@using MoonCore.Services @using MoonCore.Services
@using Moonlight.Core.Configuration @using Moonlight.Core.Configuration
@using Moonlight.Core.Services @using Moonlight.Core.Services
@using IdentityService = Moonlight.Core.Services.IdentityService
@inject ConfigService<CoreConfiguration> ConfigService @inject ConfigService<CoreConfiguration> ConfigService
@inject IdentityService IdentityService @inject IdentityService IdentityService

View File

@@ -2,7 +2,7 @@
@using MoonCore.Abstractions @using MoonCore.Abstractions
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@using MoonCore.Helpers @using MoonCore.Helpers
@using MoonCoreUI.Services
@using Moonlight.Features.Servers.Events @using Moonlight.Features.Servers.Events
@using Moonlight.Features.Servers.Helpers @using Moonlight.Features.Servers.Helpers
@using Moonlight.Features.Servers.Models.Enums @using Moonlight.Features.Servers.Models.Enums
@@ -13,13 +13,14 @@
@inject ToastService ToastService @inject ToastService ToastService
@inject AlertService AlertService @inject AlertService AlertService
@inject NavigationManager Navigation @inject NavigationManager Navigation
@inject ServerEvents ServerEvents
@implements IDisposable @implements IDisposable
<LazyLoader @ref="LazyLoader" Load="Load"> <LazyLoader @ref="LazyLoader" Load="Load">
<div class="card card-body px-5 py-3 mb-5"> <div class="card card-body px-5 py-3 mb-5">
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
<WButton OnClick="Create" CssClasses="btn btn-primary" Text="Create backup"/> <WButton OnClick="Create" CssClasses="btn btn-primary">Create backup</WButton>
</div> </div>
</div> </div>
@@ -171,23 +172,23 @@
public async Task Restore(ServerBackup backup) public async Task Restore(ServerBackup backup)
{ {
if(!await AlertService.YesNo("Do you really want to restore this backup? All files on the server will be deleted and replaced by the backup")) await AlertService.Confirm("Confirm backup restore", "Do you really want to restore this backup? All files on the server will be deleted and replaced by the backup", async () =>
return; {
await ServerService.Backup.Restore(Server, backup); await ServerService.Backup.Restore(Server, backup);
await ToastService.Success("Successfully restored backup"); await ToastService.Success("Successfully restored backup");
});
} }
public async Task Delete(ServerBackup backup, bool safeDelete = true) public async Task Delete(ServerBackup backup, bool safeDelete = true)
{ {
if(!await AlertService.YesNo("Do you really want to delete this backup? Deleted backups cannot be restored")) await AlertService.Confirm("Confirm backup deletion", "Do you really want to delete this backup? Deleted backups cannot be restored", async () =>
return; {
await ServerService.Backup.Delete(Server, backup, safeDelete); await ServerService.Backup.Delete(Server, backup, safeDelete);
await ToastService.Success("Successfully deleted backup"); await ToastService.Success("Successfully deleted backup");
await LazyLoader.Reload(); await LazyLoader.Reload();
});
} }
private async Task Download(ServerBackup backup) private async Task Download(ServerBackup backup)

View File

@@ -17,7 +17,7 @@
<div class="mt-3"> <div class="mt-3">
<div class="input-group"> <div class="input-group">
<input @bind="CommandInput" @bind:event="onchange" @onkeyup="OnEnterPressed" class="form-control form-control-transparent text-white" placeholder="Enter command"/> <input @bind="CommandInput" @bind:event="onchange" @onkeyup="OnEnterPressed" class="form-control form-control-transparent text-white" placeholder="Enter command"/>
<WButton CssClasses="btn btn-secondary rounded-start" Text="Execute" OnClick="SendCommand"/> <WButton CssClasses="btn btn-secondary rounded-start" OnClick="SendCommand">Execute</WButton>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -11,7 +11,7 @@
@implements IDisposable @implements IDisposable
<LazyLoader Load="Load" ShowAsCard="true"> <LazyLoader Load="Load">
<FileManager FileAccess="FileAccess" /> <FileManager FileAccess="FileAccess" />
</LazyLoader> </LazyLoader>

View File

@@ -3,7 +3,7 @@
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@using MoonCore.Abstractions @using MoonCore.Abstractions
@using MoonCore.Helpers @using MoonCore.Helpers
@using MoonCoreUI.Services
@inject Repository<ServerNetwork> NetworkRepository @inject Repository<ServerNetwork> NetworkRepository
@inject Repository<Server> ServerRepository @inject Repository<Server> ServerRepository
@@ -33,8 +33,8 @@
<div class="card card-body mb-15 py-3 px-5"> <div class="card card-body mb-15 py-3 px-5">
@if (!Server.DisablePublicNetwork) @if (!Server.DisablePublicNetwork)
{ {
<CrudTable TItem="ServerAllocation" ItemSource="Server.Allocations" PageSize="25" ShowPagination="false"> <MCBTable TItem="ServerAllocation" ItemSource="Server.Allocations" PageSize="25" ShowPagination="false">
<CrudColumn TItem="ServerAllocation" Field="@(x => x.IpAddress)" Title="FQDN or dedicated IP"> <MCBColumn TItem="ServerAllocation" Field="@(x => x.IpAddress)" Title="FQDN or dedicated IP">
<Template> <Template>
@if (context!.IpAddress == "0.0.0.0") @if (context!.IpAddress == "0.0.0.0")
{ {
@@ -52,8 +52,8 @@
<span>@context.IpAddress</span> <span>@context.IpAddress</span>
} }
</Template> </Template>
</CrudColumn> </MCBColumn>
<CrudColumn TItem="ServerAllocation" Field="@(x => x.IpAddress)" Title="IP address"> <MCBColumn TItem="ServerAllocation" Field="@(x => x.IpAddress)" Title="IP address">
<Template> <Template>
@if (context!.IpAddress == "0.0.0.0") @if (context!.IpAddress == "0.0.0.0")
{ {
@@ -71,14 +71,14 @@
<span>-</span> <span>-</span>
} }
</Template> </Template>
</CrudColumn> </MCBColumn>
<CrudColumn TItem="ServerAllocation" Field="@(x => x.Port)" Title="Port"/> <MCBColumn TItem="ServerAllocation" Field="@(x => x.Port)" Title="Port"/>
<CrudColumn TItem="ServerAllocation" Field="@(x => x.Note)" Title="Notes"> <MCBColumn TItem="ServerAllocation" Field="@(x => x.Note)" Title="Notes">
<Template> <Template>
<input class="form-control" placeholder="What is this allocation for?"/> <input class="form-control" placeholder="What is this allocation for?"/>
</Template> </Template>
</CrudColumn> </MCBColumn>
<CrudColumn TItem="ServerAllocation"> <MCBColumn TItem="ServerAllocation">
<Template> <Template>
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
<WButton CssClasses="btn btn-icon btn-danger disabled"> <WButton CssClasses="btn btn-icon btn-danger disabled">
@@ -86,8 +86,8 @@
</WButton> </WButton>
</div> </div>
</Template> </Template>
</CrudColumn> </MCBColumn>
</CrudTable> </MCBTable>
} }
else else
{ {

View File

@@ -3,7 +3,7 @@
@using Moonlight.Features.Servers.Models.Abstractions @using Moonlight.Features.Servers.Models.Abstractions
@using Moonlight.Features.Servers.Models.Enums @using Moonlight.Features.Servers.Models.Enums
@using Moonlight.Features.Servers.Services @using Moonlight.Features.Servers.Services
@using MoonCoreUI.Services
@implements IDisposable @implements IDisposable
@@ -20,7 +20,7 @@
</p> </p>
@if (Console.State == ServerState.Offline) @if (Console.State == ServerState.Offline)
{ {
<WButton OnClick="Reinstall" CssClasses="btn btn-primary mt-auto" Text="Reinstall"/> <WButton OnClick="Reinstall" CssClasses="btn btn-primary mt-auto">Reinstall</WButton>
} }
else else
{ {
@@ -35,7 +35,7 @@
</p> </p>
@if (Console.State == ServerState.Offline) @if (Console.State == ServerState.Offline)
{ {
<WButton OnClick="ResetServer" CssClasses="btn btn-warning mt-auto" Text="Reset"/> <WButton OnClick="ResetServer" CssClasses="btn btn-warning mt-auto">Reset</WButton>
} }
else else
{ {
@@ -43,14 +43,15 @@
} }
</div> </div>
</div> </div>
<div class="col-md-6 col-12"> @* TODO: Make deleting configurable to show or not *@ <div class="col-md-6 col-12">
@* TODO: Make deleting configurable to show or not *@
<div class="card card-body p-8 h-100"> <div class="card card-body p-8 h-100">
<p class="fs-6"> <p class="fs-6">
This deletes your server. The deleted data is not recoverable. Please make sure you have a backup of the data before deleting the server This deletes your server. The deleted data is not recoverable. Please make sure you have a backup of the data before deleting the server
</p> </p>
@if (Console.State == ServerState.Offline) @if (Console.State == ServerState.Offline)
{ {
<WButton OnClick="Delete" CssClasses="btn btn-danger mt-auto" Text="Delete"/> <WButton OnClick="Delete" CssClasses="btn btn-danger mt-auto">Delete</WButton>
} }
else else
{ {
@@ -78,17 +79,13 @@
private async Task Reinstall() private async Task Reinstall()
{ {
if (!await AlertService.YesNo("Do you want to reinstall this server? This may replace/delete some files")) await AlertService.Confirm("Confirm reinstall", "Do you want to reinstall this server? This may replace/delete some files", async () => { await ServerService.Console.SendAction(Server, PowerAction.Install); });
return;
await ServerService.Console.SendAction(Server, PowerAction.Install);
} }
private async Task ResetServer() private async Task ResetServer()
{ {
if (!await AlertService.YesNo("Do you want to reset this server? This will delete all files and run the install script")) await AlertService.Confirm("Confirm server reset", "Do you want to reset this server? This will delete all files and run the install script", async () =>
return; {
await ToastService.CreateProgress("serverReset", "Reset: Deleting files"); await ToastService.CreateProgress("serverReset", "Reset: Deleting files");
using var fileAccess = await ServerService.OpenFileAccess(Server); using var fileAccess = await ServerService.OpenFileAccess(Server);
@@ -100,28 +97,30 @@
{ {
i++; i++;
await ToastService.ModifyProgress("serverReset", $"Reset: Deleting files [{i} / {files.Length}]"); await ToastService.UpdateProgress("serverReset", $"Reset: Deleting files [{i} / {files.Length}]");
await fileAccess.Delete(fileEntry); await fileAccess.Delete(fileEntry);
} }
await ToastService.ModifyProgress("serverReset", "Reset: Starting install script"); await ToastService.UpdateProgress("serverReset", "Reset: Starting install script");
await ServerService.Console.SendAction(Server, PowerAction.Install); await ServerService.Console.SendAction(Server, PowerAction.Install);
await ToastService.RemoveProgress("serverReset"); await ToastService.DeleteProgress("serverReset");
});
} }
private async Task Delete() private async Task Delete()
{ {
var input = await AlertService.Text($"Please type '{Server.Name}' to confirm deleting this server"); await AlertService.Text("Server deletion", $"Please type '{Server.Name}' to confirm deleting this server", async input =>
{
if(input != Server.Name) if (input != Server.Name)
return; return;
await ServerService.Delete(Server); await ServerService.Delete(Server);
await ToastService.Success("Successfully deleted server"); await ToastService.Success("Successfully deleted server");
Navigation.NavigateTo("/servers"); Navigation.NavigateTo("/servers");
});
} }
private async Task OnStateChanged(ServerState _) => await InvokeAsync(StateHasChanged); private async Task OnStateChanged(ServerState _) => await InvokeAsync(StateHasChanged);

View File

@@ -2,7 +2,7 @@
@using MoonCore.Abstractions @using MoonCore.Abstractions
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@using MoonCore.Helpers @using MoonCore.Helpers
@using MoonCoreUI.Services
@using Moonlight.Features.Servers.Models.Forms.Users.Schedules @using Moonlight.Features.Servers.Models.Forms.Users.Schedules
@using Moonlight.Features.Servers.Services @using Moonlight.Features.Servers.Services
@using Newtonsoft.Json @using Newtonsoft.Json
@@ -19,11 +19,13 @@
<div class="col-md-3 col-12"> <div class="col-md-3 col-12">
<div class="card card-body p-5"> <div class="card card-body p-5">
<div class="d-flex flex-column"> <div class="d-flex flex-column">
<WButton OnClick="CreateSchedule" CssClasses="btn btn-primary my-3 mb-5" Text="Create new schedule"/> <WButton OnClick="CreateSchedule" CssClasses="btn btn-primary my-3 mb-5">Create new schedule</WButton>
@foreach (var schedule in ServerWithSchedules.Schedules) @foreach (var schedule in ServerWithSchedules.Schedules)
{ {
<WButton OnClick="() => SelectSchedule(schedule)" CssClasses="btn btn-secondary my-3" Text="@schedule.Name"/> <WButton OnClick="() => SelectSchedule(schedule)" CssClasses="btn btn-secondary my-3">
@schedule.Name
</WButton>
} }
</div> </div>
</div> </div>
@@ -158,15 +160,12 @@
</div> </div>
</LazyLoader> </LazyLoader>
<FormModalLauncher @ref="Launcher"/>
@code @code
{ {
[CascadingParameter] public Server Server { get; set; } [CascadingParameter] public Server Server { get; set; }
private Server ServerWithSchedules; private Server ServerWithSchedules;
private LazyLoader LazyLoader; private LazyLoader LazyLoader;
private FormModalLauncher Launcher;
private ServerSchedule? SelectedSchedule; private ServerSchedule? SelectedSchedule;
private List<ServerScheduleItem> SortedItems = new(); private List<ServerScheduleItem> SortedItems = new();
@@ -208,11 +207,11 @@
private async Task CreateSchedule() private async Task CreateSchedule()
{ {
await Launcher.Show<CreateScheduleForm>("Create a new schedule", async form => await AlertService.Text("New schedule", "Create a new schedule", async name =>
{ {
ServerWithSchedules.Schedules.Add(new() ServerWithSchedules.Schedules.Add(new()
{ {
Name = form.Name Name = name
}); });
ServerRepository.Update(ServerWithSchedules); ServerRepository.Update(ServerWithSchedules);
@@ -243,7 +242,8 @@
return; return;
} }
await Launcher.Show("Configure action", async formData => { await AddScheduleAction(NewItemActionType, SelectedSchedule.Items.Count, formData); }, action.FormType); // TODO: Redo everything here
//await Launcher.Show("Configure action", async formData => { await AddScheduleAction(NewItemActionType, SelectedSchedule.Items.Count, formData); }, action.FormType);
} }
private async Task AddScheduleAction(string type, int priority, object data) private async Task AddScheduleAction(string type, int priority, object data)
@@ -276,7 +276,7 @@
var action = ScheduleService.Actions.First(x => x.Key == item.Action).Value; var action = ScheduleService.Actions.First(x => x.Key == item.Action).Value;
var formModel = JsonConvert.DeserializeObject(item.DataJson, action.FormType)!; var formModel = JsonConvert.DeserializeObject(item.DataJson, action.FormType)!;
/*
await Launcher.Show("Configure action", async formData => await Launcher.Show("Configure action", async formData =>
{ {
item.DataJson = JsonConvert.SerializeObject(formData); item.DataJson = JsonConvert.SerializeObject(formData);
@@ -284,7 +284,7 @@
ScheduleItemRepository.Update(item); ScheduleItemRepository.Update(item);
await ToastService.Success("Successfully updated action"); await ToastService.Success("Successfully updated action");
}, action.FormType, formModel: formModel); }, action.FormType, formModel: formModel);*/
} }
private async Task MoveItem(ServerScheduleItem item, int move) private async Task MoveItem(ServerScheduleItem item, int move)
@@ -312,9 +312,8 @@
if (SelectedSchedule == null) if (SelectedSchedule == null)
return; return;
if (!await AlertService.YesNo("Do you really want to delete this action? This cannot be undone")) await AlertService.Confirm("Confirm schedule item deletion", "Do you really want to delete this action? This cannot be undone", async () =>
return; {
SortedItems.Remove(item); SortedItems.Remove(item);
SelectedSchedule.Items.Remove(item); SelectedSchedule.Items.Remove(item);
@@ -324,6 +323,7 @@
await ToastService.Success("Successfully deleted action"); await ToastService.Success("Successfully deleted action");
await LazyLoader.Reload(); await LazyLoader.Reload();
});
} }
private Task FixPriorities() private Task FixPriorities()
@@ -346,9 +346,8 @@
if (SelectedSchedule == null) if (SelectedSchedule == null)
return; return;
if (!await AlertService.YesNo($"Do you really want to delete the schedule '{SelectedSchedule.Name}'? This cannot be undone")) await AlertService.Confirm("Confirm schedule deletion", $"Do you really want to delete the schedule '{SelectedSchedule.Name}'? This cannot be undone", async () =>
return; {
foreach (var item in SelectedSchedule.Items.ToArray()) foreach (var item in SelectedSchedule.Items.ToArray())
{ {
try try
@@ -367,6 +366,7 @@
await ToastService.Success("Successfully deleted schedule"); await ToastService.Success("Successfully deleted schedule");
await LazyLoader.Reload(); await LazyLoader.Reload();
});
} }
private async Task RunSelectedSchedule() private async Task RunSelectedSchedule()
@@ -378,7 +378,7 @@
var result = await ScheduleService.Run(Server, SelectedSchedule); var result = await ScheduleService.Run(Server, SelectedSchedule);
await ToastService.RemoveProgress("scheduleRun"); await ToastService.DeleteProgress("scheduleRun");
if (result.Failed) if (result.Failed)
await ToastService.Danger($"Schedule run failed ({result.ExecutionSeconds}s)"); await ToastService.Danger($"Schedule run failed ({result.ExecutionSeconds}s)");

View File

@@ -2,7 +2,7 @@
@using Moonlight.Features.Servers.UI.Components.VariableViews @using Moonlight.Features.Servers.UI.Components.VariableViews
@using MoonCore.Abstractions @using MoonCore.Abstractions
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@using MoonCoreUI.Services
@using Moonlight.Features.Servers.Entities.Enums @using Moonlight.Features.Servers.Entities.Enums
@inject Repository<Server> ServerRepository @inject Repository<Server> ServerRepository
@@ -10,14 +10,14 @@
@inject Repository<ServerVariable> ServerVariableRepository @inject Repository<ServerVariable> ServerVariableRepository
@inject ToastService ToastService @inject ToastService ToastService
<LazyLoader @ref="LazyLoader" Load="Load" ShowAsCard="true"> <LazyLoader @ref="LazyLoader" Load="Load">
<div class="row mt-1 g-5"> <div class="row mt-1 g-5">
<div class="col-md-6 col-12"> <div class="col-md-6 col-12">
<div class="card card-body p-5 h-100"> <div class="card card-body p-5 h-100">
<label class="form-label fs-5">Docker image</label> <label class="form-label fs-5">Docker image</label>
@if (Image.AllowDockerImageChange) @if (Image.AllowDockerImageChange)
{ {
<SmartSelect @bind-Value="SelectedDockerImage" <MCBSelect @bind-Value="SelectedDockerImage"
Items="Image.DockerImages" Items="Image.DockerImages"
DisplayField="@(x => x.DisplayName)" DisplayField="@(x => x.DisplayName)"
OnChange="OnDockerImageChanged"/> OnChange="OnDockerImageChanged"/>

View File

@@ -4,7 +4,7 @@
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@using MoonCore.Abstractions @using MoonCore.Abstractions
@using MoonCore.Exceptions @using MoonCore.Exceptions
@using MoonCoreUI.Services
@using Moonlight.Features.Servers.Entities @using Moonlight.Features.Servers.Entities
@using Moonlight.Features.Servers.Helpers @using Moonlight.Features.Servers.Helpers
@using Moonlight.Features.Servers.Models.Forms.Admin.Images @using Moonlight.Features.Servers.Models.Forms.Admin.Images
@@ -17,9 +17,10 @@
@inject Repository<ServerImage> ImageRepository @inject Repository<ServerImage> ImageRepository
@inject ImageConversionHelper ImageConversionHelper @inject ImageConversionHelper ImageConversionHelper
@inject FileDownloadService FileDownloadService @inject DownloadService DownloadService
@inject ToastService ToastService @inject ToastService ToastService
@inject AlertService AlertService @inject AlertService AlertService
@inject ILogger<Index> Logger
@attribute [RequirePermission(5002)] @attribute [RequirePermission(5002)]
@@ -34,14 +35,14 @@
CustomDelete="CustomDelete" CustomDelete="CustomDelete"
@ref="Crud"> @ref="Crud">
<View> <View>
<CrudColumn TItem="ServerImage" Field="@(x => x.Id)" Title="Id" Filterable="true"/> <MCBColumn TItem="ServerImage" Field="@(x => x.Id)" Title="Id" Filterable="true"/>
<CrudColumn TItem="ServerImage" Field="@(x => x.Name)" Title="Name" Filterable="true"> <MCBColumn TItem="ServerImage" Field="@(x => x.Name)" Title="Name" Filterable="true">
<Template> <Template>
<a href="/admin/servers/images/view/@context!.Id">@context.Name</a> <a href="/admin/servers/images/view/@context!.Id">@context.Name</a>
</Template> </Template>
</CrudColumn> </MCBColumn>
<CrudColumn TItem="ServerImage" Field="@(x => x.Author)" Title="Author" Filterable="true"/> <MCBColumn TItem="ServerImage" Field="@(x => x.Author)" Title="Author" Filterable="true"/>
<CrudColumn TItem="ServerImage"> <MCBColumn TItem="ServerImage">
<Template> <Template>
<div class="text-end"> <div class="text-end">
@if (!string.IsNullOrEmpty(context.UpdateUrl)) @if (!string.IsNullOrEmpty(context.UpdateUrl))
@@ -66,8 +67,8 @@
</a> </a>
</div> </div>
</Template> </Template>
</CrudColumn> </MCBColumn>
</View> </View>@*
<NoItemsView> <NoItemsView>
<IconAlert Title="No images found" Color="primary" Icon="bx-search-alt"> <IconAlert Title="No images found" Color="primary" Icon="bx-search-alt">
Download and import a image from our <a href="https://github.com/Moonlight-Panel/Images">repository</a> or create a new one. Need help? Check out our <a href="https://docs.moonlightpanel.xyz">documentation</a> Download and import a image from our <a href="https://github.com/Moonlight-Panel/Images">repository</a> or create a new one. Need help? Check out our <a href="https://docs.moonlightpanel.xyz">documentation</a>
@@ -84,14 +85,14 @@
Import Import
</a> </a>
</SmartCustomFileSelect> </SmartCustomFileSelect>
</OverviewToolbar> </OverviewToolbar>*@
</AutoCrud> </AutoCrud>
@code @code
{ {
private AutoCrud<ServerImage, CreateImageForm, UpdateImageForm> Crud; private AutoCrud<ServerImage, CreateImageForm, UpdateImageForm> Crud;
private SmartCustomFileSelect ImageUpload; private MCBCustomFileSelect ImageUpload;
private SmartCustomFileSelect EggUpload; private MCBCustomFileSelect EggUpload;
private IEnumerable<ServerImage> Load(Repository<ServerImage> repository) private IEnumerable<ServerImage> Load(Repository<ServerImage> repository)
{ {
@@ -178,7 +179,7 @@
{ {
var json = await ImageConversionHelper.ExportAsJson(image); var json = await ImageConversionHelper.ExportAsJson(image);
var imageName = image.Name.Replace(" ", ""); var imageName = image.Name.Replace(" ", "");
await FileDownloadService.DownloadString($"{imageName}.json", json); await DownloadService.DownloadString($"{imageName}.json", json);
await ToastService.Success($"Successfully exported '{image.Name}'"); await ToastService.Success($"Successfully exported '{image.Name}'");
} }
@@ -198,7 +199,7 @@
await ToastService.Success($"Successfully imported '{image.Name}'"); await ToastService.Success($"Successfully imported '{image.Name}'");
await ImageUpload.RemoveSelection(); await ImageUpload.RemoveSelection();
await Crud.Reload(); //await Crud.Reload();
} }
catch (DisplayException) catch (DisplayException)
{ {
@@ -206,8 +207,7 @@
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warn("An error occured while importing a image"); Logger.LogWarning("An error occured while importing a image: {e}", e);
Logger.Warn(e);
await ToastService.Danger("Unable to import egg: " + e.Message); await ToastService.Danger("Unable to import egg: " + e.Message);
} }
@@ -219,16 +219,9 @@
private async Task ImportEgg(IBrowserFile file) private async Task ImportEgg(IBrowserFile file)
{ {
var confirm = await AlertService.YesNo("Importing pterodactyl eggs is a experimental feature and may result in unusable images. Are you sure you want to proceed?", await AlertService.Confirm("Import a pterodactyl egg", "Importing pterodactyl eggs is a experimental feature and may result in unusable images. Are you sure you want to proceed?",
"Yes, i take the risk", async () =>
"Cancel");
if (!confirm)
{ {
await EggUpload.RemoveSelection();
return;
}
try try
{ {
var stream = file.OpenReadStream(); var stream = file.OpenReadStream();
@@ -242,7 +235,7 @@
await ToastService.Success($"Successfully imported '{image.Name}'"); await ToastService.Success($"Successfully imported '{image.Name}'");
await EggUpload.RemoveSelection(); await EggUpload.RemoveSelection();
await Crud.Reload(); //await Crud.Reload();
} }
catch (DisplayException) catch (DisplayException)
{ {
@@ -250,8 +243,7 @@
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warn("An error occured while importing a pterodactyl egg"); Logger.LogWarning("An error occured while importing a pterodactyl egg: {e}", e);
Logger.Warn(e);
await ToastService.Danger("Unable to import egg: " + e.Message); await ToastService.Danger("Unable to import egg: " + e.Message);
} }
@@ -259,5 +251,7 @@
{ {
await EggUpload.RemoveSelection(); await EggUpload.RemoveSelection();
} }
},
"Yes, i take the risk");
} }
} }

View File

@@ -1,7 +1,7 @@
@page "/admin/servers/images/view/{Id:int}/{Route?}" @page "/admin/servers/images/view/{Id:int}/{Route?}"
@using Mappy.Net @using Mappy.Net
@using MoonCoreUI.Services
@using MoonCore.Abstractions @using MoonCore.Abstractions
@using Moonlight.Features.Servers.Entities @using Moonlight.Features.Servers.Entities
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore

View File

@@ -4,9 +4,7 @@
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@using MoonCore.Abstractions @using MoonCore.Abstractions
@using MoonCore.Exceptions @using MoonCore.Exceptions
@using MoonCore.Helpers
@using MoonCoreUI.Models
@using MoonCoreUI.Services
@using Moonlight.Features.Servers.Entities @using Moonlight.Features.Servers.Entities
@using Moonlight.Features.Servers.Models.Enums @using Moonlight.Features.Servers.Models.Enums
@using Moonlight.Features.Servers.Models.Forms.Admin.Servers @using Moonlight.Features.Servers.Models.Forms.Admin.Servers
@@ -15,11 +13,12 @@
@inject ServerService ServerService @inject ServerService ServerService
@inject Repository<Server> ServerRepository @inject Repository<Server> ServerRepository
@inject ToastService ToastService @inject ToastService ToastService
@inject ILogger<Index> Logger
@attribute [RequirePermission(5000)] @attribute [RequirePermission(5000)]
<AdminServersNavigation Index="0"/> <AdminServersNavigation Index="0"/>
@*
<AutoCrud TItem="Server" <AutoCrud TItem="Server"
TCreateForm="CreateServerForm" TCreateForm="CreateServerForm"
TUpdateForm="CreateServerForm" TUpdateForm="CreateServerForm"
@@ -30,40 +29,41 @@
CustomDelete="CustomDelete" CustomDelete="CustomDelete"
OnConfigure="OnConfigure"> OnConfigure="OnConfigure">
<View> <View>
<CrudColumn TItem="Server" Field="@(x => x.Id)" Title="Id" Filterable="true"/> <MCBColumn TItem="Server" Field="@(x => x.Id)" Title="Id" Filterable="true"/>
<CrudColumn TItem="Server" Field="@(x => x.Name)" Title="Name" Filterable="true"/> <MCBColumn TItem="Server" Field="@(x => x.Name)" Title="Name" Filterable="true"/>
<CrudColumn TItem="Server" Field="@(x => x.Id)" Title="Image"> <MCBColumn TItem="Server" Field="@(x => x.Id)" Title="Image">
<Template> <Template>
<span>@(context.Image.Name)</span> <span>@(context.Image.Name)</span>
</Template> </Template>
</CrudColumn> </MCBColumn>
<CrudColumn TItem="Server" Field="@(x => x.Id)" Title="Node"> <MCBColumn TItem="Server" Field="@(x => x.Id)" Title="Node">
<Template> <Template>
<span>@(context.Node.Name)</span> <span>@(context.Node.Name)</span>
</Template> </Template>
</CrudColumn> </MCBColumn>
<CrudColumn TItem="Server" Field="@(x => x.Id)" Title="User"> <MCBColumn TItem="Server" Field="@(x => x.Id)" Title="User">
<Template> <Template>
<span>@(context.Owner.Username)</span> <span>@(context.Owner.Username)</span>
</Template> </Template>
</CrudColumn> </MCBColumn>
</View> </View>
<NoItemsView> <NoItemsView>
<IconAlert Title="No servers found" Color="primary" Icon="bx-search-alt"> <IconAlert Title="No servers found" Color="primary" Icon="bx-search-alt">
Create a new server in order to manage it using this page. Need help? Check out our <a href="https://docs.moonlightpanel.xyz">documentation</a> Create a new server in order to manage it using this page. Need help? Check out our <a href="https://docs.moonlightpanel.xyz">documentation</a>
</IconAlert> </IconAlert>
</NoItemsView> </NoItemsView>
</AutoCrud> </AutoCrud>*@
@code @code
{ {
/*
private void OnConfigure(AutoCrudOptions options) private void OnConfigure(AutoCrudOptions options)
{ {
options.AddCustomItemLoader<Server, ServerAllocation>("FreeAllocations", LoadFreeAllocations); options.AddCustomItemLoader<Server, ServerAllocation>("FreeAllocations", LoadFreeAllocations);
options.AddCustomDisplayFunction<ServerAllocation>("AllocationWithIp", options.AddCustomDisplayFunction<ServerAllocation>("AllocationWithIp",
allocation => allocation.IpAddress + ":" + allocation.Port); allocation => allocation.IpAddress + ":" + allocation.Port);
} }*/
private IEnumerable<Server> Load(Repository<Server> repository) private IEnumerable<Server> Load(Repository<Server> repository)
{ {
@@ -124,8 +124,7 @@
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Error("Unable to sync server changes due to an error occuring"); Logger.LogError("Unable to sync server changes due to an error occuring: {e}", e);
Logger.Error(e);
await ToastService.Danger("An error occured while sending the changes to the daemon"); await ToastService.Danger("An error occured while sending the changes to the daemon");
} }

View File

@@ -2,7 +2,7 @@
@using MoonCore.Abstractions @using MoonCore.Abstractions
@using MoonCore.Helpers @using MoonCore.Helpers
@using MoonCoreUI.Services
@using Moonlight.Features.Servers.Api.Resources @using Moonlight.Features.Servers.Api.Resources
@using Moonlight.Features.Servers.Entities @using Moonlight.Features.Servers.Entities
@using Moonlight.Features.Servers.Services @using Moonlight.Features.Servers.Services
@@ -18,6 +18,7 @@
@inject ServerService ServerService @inject ServerService ServerService
@inject ToastService ToastService @inject ToastService ToastService
@inject AlertService AlertService @inject AlertService AlertService
@inject ILogger<Manager> Logger
@attribute [RequirePermission(5000)] @attribute [RequirePermission(5000)]
@@ -210,8 +211,7 @@
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warn($"An error occured while fetching server list from node {node.Id}"); Logger.LogWarning("An error occured while fetching server list from node {nodeId}: {e}", node.Id, e);
Logger.Warn(e);
OfflineNodes.Add(node); OfflineNodes.Add(node);
@@ -248,9 +248,8 @@
await Table.SetPageSizeAsync(PageSize); await Table.SetPageSizeAsync(PageSize);
// Confirm // Confirm
if(!await AlertService.YesNo($"Do you really want to perform the action '{action}' for {items.Length} servers?")) await AlertService.Confirm("Confirm power action", $"Do you really want to perform the action '{action}' for {items.Length} servers?", async () =>
return; {
await ToastService.CreateProgress("multiPowerAction", "Preparing"); await ToastService.CreateProgress("multiPowerAction", "Preparing");
// Perform // Perform
@@ -259,22 +258,22 @@
{ {
try try
{ {
await ToastService.ModifyProgress("multiPowerAction", $"Sending power action [{i + 1} / {items.Length}]"); await ToastService.UpdateProgress("multiPowerAction", $"Sending power action [{i + 1} / {items.Length}]");
await ServerService.Console.SendAction(item.Server, action); await ServerService.Console.SendAction(item.Server, action);
i++; i++;
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warn($"An error occured while performing power action on server {item.Server.Id}"); Logger.LogWarning("An error occured while performing power action on server {serverId}: {e}", item.Server.Id, e);
Logger.Warn(e);
await ToastService.Danger($"Unable to perform power action for server '{item.Server.Name}'"); await ToastService.Danger($"Unable to perform power action for server '{item.Server.Name}'");
} }
} }
await ToastService.RemoveProgress("multiPowerAction"); await ToastService.DeleteProgress("multiPowerAction");
await ToastService.Success($"Successfully performed the action for {i} servers"); await ToastService.Success($"Successfully performed the action for {i} servers");
});
} }
public void Dispose() public void Dispose()

View File

@@ -15,6 +15,7 @@
@inject Repository<ServerNode> NodeRepository @inject Repository<ServerNode> NodeRepository
@inject NodeService NodeService @inject NodeService NodeService
@inject IServiceProvider ServiceProvider @inject IServiceProvider ServiceProvider
@inject ILogger<Index> Logger
@implements IDisposable @implements IDisposable
@@ -31,14 +32,14 @@
ValidateUpdate="ValidateUpdate" ValidateUpdate="ValidateUpdate"
ValidateDelete="ValidateDelete"> ValidateDelete="ValidateDelete">
<View> <View>
<CrudColumn TItem="ServerNode" Field="@(x => x.Id)" Title="Id"/> <MCBColumn TItem="ServerNode" Field="@(x => x.Id)" Title="Id"/>
<CrudColumn TItem="ServerNode" Field="@(x => x.Name)" Title="Name"> <MCBColumn TItem="ServerNode" Field="@(x => x.Name)" Title="Name">
<Template> <Template>
<a href="/admin/servers/nodes/view/@(context!.Id)">@context!.Name</a> <a href="/admin/servers/nodes/view/@(context!.Id)">@context!.Name</a>
</Template> </Template>
</CrudColumn> </MCBColumn>
<CrudColumn TItem="ServerNode" Field="@(x => x.Fqdn)" Title="Fqdn"/> <MCBColumn TItem="ServerNode" Field="@(x => x.Fqdn)" Title="Fqdn"/>
<CrudColumn TItem="ServerNode" Title="Status"> <MCBColumn TItem="ServerNode" Title="Status">
<Template> <Template>
@if (NodeStats.ContainsKey(context!.Id)) @if (NodeStats.ContainsKey(context!.Id))
{ {
@@ -56,8 +57,8 @@
<span class="text-muted">N/A</span> <span class="text-muted">N/A</span>
} }
</Template> </Template>
</CrudColumn> </MCBColumn>
<CrudColumn TItem="ServerNode" Title="CPU"> <MCBColumn TItem="ServerNode" Title="CPU">
<Template> <Template>
@if (NodeStats.ContainsKey(context!.Id) && NodeStats[context.Id] != null) @if (NodeStats.ContainsKey(context!.Id) && NodeStats[context.Id] != null)
{ {
@@ -71,8 +72,8 @@
<span class="text-muted">N/A</span> <span class="text-muted">N/A</span>
} }
</Template> </Template>
</CrudColumn> </MCBColumn>
<CrudColumn TItem="ServerNode" Title="Memory"> <MCBColumn TItem="ServerNode" Title="Memory">
<Template> <Template>
@if (NodeStats.ContainsKey(context!.Id) && NodeStats[context.Id] != null) @if (NodeStats.ContainsKey(context!.Id) && NodeStats[context.Id] != null)
{ {
@@ -90,8 +91,8 @@
<span class="text-muted">N/A</span> <span class="text-muted">N/A</span>
} }
</Template> </Template>
</CrudColumn> </MCBColumn>
<CrudColumn TItem="ServerNode" Title="Disk"> <MCBColumn TItem="ServerNode" Title="Disk">
<Template> <Template>
@if (NodeStats.ContainsKey(context!.Id) && NodeStats[context.Id] != null) @if (NodeStats.ContainsKey(context!.Id) && NodeStats[context.Id] != null)
{ {
@@ -105,13 +106,14 @@
<span class="text-muted">N/A</span> <span class="text-muted">N/A</span>
} }
</Template> </Template>
</CrudColumn> </MCBColumn>
</View> </View>
@*
<NoItemsView> <NoItemsView>
<IconAlert Title="No nodes found" Color="primary" Icon="bx-search-alt"> <IconAlert Title="No nodes found" Color="primary" Icon="bx-search-alt">
Add a new node in order to get started. Need help? Check out our <a href="https://docs.moonlightpanel.xyz">documentation</a> Add a new node in order to get started. Need help? Check out our <a href="https://docs.moonlightpanel.xyz">documentation</a>
</IconAlert> </IconAlert>
</NoItemsView> </NoItemsView>*@
</AutoCrud> </AutoCrud>
</LazyLoader> </LazyLoader>
@@ -140,8 +142,7 @@
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warn($"Unable to fetch system status for node '{node.Name}'"); Logger.LogWarning("Unable to fetch system status for node '{name}': {e}", node.Name, e);
Logger.Warn(e);
NodeStats[node.Id] = null; NodeStats[node.Id] = null;
} }

View File

@@ -14,6 +14,7 @@
@inject Repository<Server> ServerRepository @inject Repository<Server> ServerRepository
@inject IdentityService IdentityService @inject IdentityService IdentityService
@inject ServerService ServerService @inject ServerService ServerService
@inject ILogger<Index> Logger
<ServersNavigation Index="0"/> <ServersNavigation Index="0"/>
@@ -163,8 +164,7 @@
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warn($"Unable to get server state for server {server.Id}"); Logger.LogWarning("Unable to get server state for server {serverId}: {e}", server.Id, e);
Logger.Warn(e);
} }
} }
}); });
@@ -187,8 +187,7 @@
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warn($"Unable to get server stats for server {server.Id}"); Logger.LogWarning("Unable to get server stats for server {serverId}: {e}", server.Id, e);
Logger.Warn(e);
} }
} }
}); });

View File

@@ -19,13 +19,13 @@
Loader="Load" Loader="Load"
ValidateAdd="ValidateAdd"> ValidateAdd="ValidateAdd">
<View> <View>
<CrudColumn TItem="ServerNetwork" Field="@(x => x.Name)" Title="Name"/> <MCBColumn TItem="ServerNetwork" Field="@(x => x.Name)" Title="Name"/>
<CrudColumn TItem="ServerNetwork" Title="Node"> <MCBColumn TItem="ServerNetwork" Title="Node">
<Template> <Template>
<span>@(context!.Node.Name)</span> <span>@(context!.Node.Name)</span>
</Template> </Template>
</CrudColumn> </MCBColumn>
<CrudColumn TItem="ServerNetwork" Title="Used by"> <MCBColumn TItem="ServerNetwork" Title="Used by">
<Template> <Template>
@{ @{
var servers = UsedByCache.ContainsKey(context.Id) ? UsedByCache[context.Id] : Array.Empty<Server>(); var servers = UsedByCache.ContainsKey(context.Id) ? UsedByCache[context.Id] : Array.Empty<Server>();
@@ -38,13 +38,13 @@
} }
</span> </span>
</Template> </Template>
</CrudColumn> </MCBColumn>
</View> </View>@*
<NoItemsView> <NoItemsView>
<IconAlert Icon="bx-search-alt" Color="primary" Title="No private network found"> <IconAlert Icon="bx-search-alt" Color="primary" Title="No private network found">
Create a new private network in order to connect multiple servers on the same node Create a new private network in order to connect multiple servers on the same node
</IconAlert> </IconAlert>
</NoItemsView> </NoItemsView>*@
</AutoCrud> </AutoCrud>
@code @code

View File

@@ -93,8 +93,8 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="MoonCore" Version="1.3.5" /> <PackageReference Include="MoonCore" Version="1.3.8" />
<PackageReference Include="MoonCoreUI" Version="1.2.1" /> <PackageReference Include="MoonCore.Blazor" Version="1.0.3" />
<PackageReference Include="Otp.NET" Version="1.3.0" /> <PackageReference Include="Otp.NET" Version="1.3.0" />
<PackageReference Include="QRCoder" Version="1.4.3" /> <PackageReference Include="QRCoder" Version="1.4.3" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.6.2" /> <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.6.2" />

View File

@@ -37,6 +37,8 @@
<!-- TODO: Replace with local version --> <!-- TODO: Replace with local version -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300,400,500,600,700"> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300,400,500,600,700">
<link href="/_content/MoonCore.Blazor/css/mooncore.blazor.css" rel="stylesheet" type="text/css"/>
<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered"/> <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered"/>
</head> </head>
<body data-kt-app-header-fixed="true" <body data-kt-app-header-fixed="true"
@@ -62,6 +64,7 @@
<!-- BlazorTable Interop --> <!-- BlazorTable Interop -->
<script src="/_content/BlazorTable/BlazorTable.min.js"></script> <script src="/_content/BlazorTable/BlazorTable.min.js"></script>
<script src="/_content/MoonCore.Blazor/js/mooncore.blazor.js"></script>
<!-- Blazor server script --> <!-- Blazor server script -->
<script src="/_framework/blazor.server.js"></script> <script src="/_framework/blazor.server.js"></script>

Some files were not shown because too many files have changed in this diff Show More