diff --git a/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelPage.cshtml index 8aa31717..53e82fcd 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelPage.cshtml @@ -11,6 +11,17 @@ Model.Title = "Admin Panel"; } +@if (Model.Log != null) +{ +
+

Command Output

+ @foreach (string line in Model.Log.Split("\n")) + { + @line.TrimEnd()
+ } +
+} + @if (!this.Request.IsMobile()) {
diff --git a/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelPage.cshtml.cs index cb42034e..6df20cfc 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelPage.cshtml.cs @@ -1,7 +1,9 @@ #nullable enable using LBPUnion.ProjectLighthouse.Administration; using LBPUnion.ProjectLighthouse.Administration.Maintenance; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; @@ -17,7 +19,9 @@ public class AdminPanelPage : BaseLayout public List Statistics = new(); - public async Task OnGet([FromQuery] string? args, [FromQuery] string? command, [FromQuery] string? maintenanceJob) + public string? Log; + + public async Task OnGet([FromQuery] string? args, [FromQuery] string? command, [FromQuery] string? maintenanceJob, [FromQuery] string? log) { User? user = this.Database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("~/login"); @@ -33,8 +37,9 @@ public class AdminPanelPage : BaseLayout args ??= ""; args = command + " " + args; string[] split = args.Split(" "); - await MaintenanceHelper.RunCommand(split); - return this.Redirect("~/admin"); + + List runCommand = await MaintenanceHelper.RunCommand(split); + return this.Redirect($"~/admin?log={CryptoHelper.ToBase64(runCommand.ToLogString())}"); } if (!string.IsNullOrEmpty(maintenanceJob)) @@ -43,6 +48,11 @@ public class AdminPanelPage : BaseLayout return this.Redirect("~/admin"); } + if (!string.IsNullOrEmpty(log)) + { + this.Log = CryptoHelper.FromBase64(log); + } + return this.Page(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Administration/Maintenance/Commands/CreateUserCommand.cs b/ProjectLighthouse/Administration/Maintenance/Commands/CreateUserCommand.cs index 2d433e59..39b86e04 100644 --- a/ProjectLighthouse/Administration/Maintenance/Commands/CreateUserCommand.cs +++ b/ProjectLighthouse/Administration/Maintenance/Commands/CreateUserCommand.cs @@ -11,7 +11,7 @@ public class CreateUserCommand : ICommand { private readonly Database _database = new(); - public async Task Run(string[] args) + public async Task Run(string[] args, Logger logger) { string onlineId = args[0]; string password = args[1]; @@ -22,17 +22,17 @@ public class CreateUserCommand : ICommand if (user == null) { user = await this._database.CreateUser(onlineId, CryptoHelper.BCryptHash(password)); - Logger.Success($"Created user {user.UserId} with online ID (username) {user.Username} and the specified password.", LogArea.Login); + logger.LogSuccess($"Created user {user.UserId} with online ID (username) {user.Username} and the specified password.", LogArea.Command); user.PasswordResetRequired = true; - Logger.Info("This user will need to reset their password when they log in.", LogArea.Login); + logger.LogInfo("This user will need to reset their password when they log in.", LogArea.Command); await this._database.SaveChangesAsync(); - Logger.Info("Database changes saved.", LogArea.Database); + logger.LogInfo("Database changes saved.", LogArea.Command); } else { - Logger.Error("A user with this username already exists.", LogArea.Login); + logger.LogError("A user with this username already exists.", LogArea.Command); } } diff --git a/ProjectLighthouse/Administration/Maintenance/Commands/DeleteUserCommand.cs b/ProjectLighthouse/Administration/Maintenance/Commands/DeleteUserCommand.cs index 880b12a4..cb783dcc 100644 --- a/ProjectLighthouse/Administration/Maintenance/Commands/DeleteUserCommand.cs +++ b/ProjectLighthouse/Administration/Maintenance/Commands/DeleteUserCommand.cs @@ -1,6 +1,7 @@ #nullable enable using System; using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using Microsoft.EntityFrameworkCore; @@ -17,7 +18,7 @@ public class DeleteUserCommand : ICommand }; public string Arguments() => ""; public int RequiredArgs() => 1; - public async Task Run(string[] args) + public async Task Run(string[] args, Logger logger) { User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]); if (user == null) @@ -28,7 +29,7 @@ public class DeleteUserCommand : ICommand } catch { - Console.WriteLine($"Could not find user by parameter '{args[0]}'"); + logger.LogError($"Could not find user by parameter '{args[0]}'", LogArea.Command); return; } diff --git a/ProjectLighthouse/Administration/Maintenance/Commands/MakeUserAdminCommand.cs b/ProjectLighthouse/Administration/Maintenance/Commands/MakeUserAdminCommand.cs index 2741a86d..0cc0f85f 100644 --- a/ProjectLighthouse/Administration/Maintenance/Commands/MakeUserAdminCommand.cs +++ b/ProjectLighthouse/Administration/Maintenance/Commands/MakeUserAdminCommand.cs @@ -1,6 +1,7 @@ #nullable enable using System; using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using Microsoft.EntityFrameworkCore; @@ -19,7 +20,7 @@ public class MakeUserAdminCommand : ICommand public string Arguments() => ""; public int RequiredArgs() => 1; - public async Task Run(string[] args) + public async Task Run(string[] args, Logger logger) { User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]); if (user == null) @@ -30,13 +31,13 @@ public class MakeUserAdminCommand : ICommand } catch { - Console.WriteLine($"Could not find user by parameter '{args[0]}'"); + logger.LogError($"Could not find user by parameter '{args[0]}'", LogArea.Command); return; } user.IsAdmin = true; await this.database.SaveChangesAsync(); - Console.WriteLine($"The user {user.Username} (id: {user.UserId}) is now an admin."); + logger.LogSuccess($"The user {user.Username} (id: {user.UserId}) is now an admin.", LogArea.Command); } } \ No newline at end of file diff --git a/ProjectLighthouse/Administration/Maintenance/Commands/RenameUserCommand.cs b/ProjectLighthouse/Administration/Maintenance/Commands/RenameUserCommand.cs index 875b0539..3995e023 100644 --- a/ProjectLighthouse/Administration/Maintenance/Commands/RenameUserCommand.cs +++ b/ProjectLighthouse/Administration/Maintenance/Commands/RenameUserCommand.cs @@ -1,6 +1,7 @@ #nullable enable using System; using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using Microsoft.EntityFrameworkCore; @@ -9,7 +10,7 @@ namespace LBPUnion.ProjectLighthouse.Administration.Maintenance.Commands; public class RenameUserCommand : ICommand { private readonly Database database = new(); - public async Task Run(string[] args) + public async Task Run(string[] args, Logger logger) { User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]); if (user == null) @@ -20,14 +21,14 @@ public class RenameUserCommand : ICommand } catch { - Console.WriteLine($"Could not find user by parameter '{args[0]}'"); + logger.LogError($"Could not find user by parameter '{args[0]}'", LogArea.Command); return; } user.Username = args[1]; await this.database.SaveChangesAsync(); - Console.WriteLine($"The username for user {user.Username} (id: {user.UserId}) has been changed."); + logger.LogSuccess($"The username for user {user.Username} (id: {user.UserId}) has been changed.", LogArea.Command); } public string Name() => "Rename User"; public string[] Aliases() diff --git a/ProjectLighthouse/Administration/Maintenance/Commands/ResetPasswordCommand.cs b/ProjectLighthouse/Administration/Maintenance/Commands/ResetPasswordCommand.cs index e5d2c144..0433f866 100644 --- a/ProjectLighthouse/Administration/Maintenance/Commands/ResetPasswordCommand.cs +++ b/ProjectLighthouse/Administration/Maintenance/Commands/ResetPasswordCommand.cs @@ -2,6 +2,7 @@ using System; using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using Microsoft.EntityFrameworkCore; @@ -19,7 +20,7 @@ public class ResetPasswordCommand : ICommand public string Arguments() => " "; public int RequiredArgs() => 2; - public async Task Run(string[] args) + public async Task Run(string[] args, Logger logger) { User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]); if (user == null) @@ -30,9 +31,10 @@ public class ResetPasswordCommand : ICommand } catch { - Console.WriteLine($"Could not find user by parameter '{args[0]}'"); + logger.LogError($"Could not find user by parameter '{args[0]}'", LogArea.Command); return; } + string password = args[1]; if (password.Length != 64) password = CryptoHelper.Sha256Hash(password); @@ -41,6 +43,6 @@ public class ResetPasswordCommand : ICommand await this.database.SaveChangesAsync(); - Console.WriteLine($"The password for user {user.Username} (id: {user.UserId}) has been reset."); + logger.LogSuccess($"The password for user {user.Username} (id: {user.UserId}) has been reset.", LogArea.Command); } } \ No newline at end of file diff --git a/ProjectLighthouse/Administration/Maintenance/Commands/TestWebhookCommand.cs b/ProjectLighthouse/Administration/Maintenance/Commands/TestWebhookCommand.cs index 9beadef8..c6c70bf4 100644 --- a/ProjectLighthouse/Administration/Maintenance/Commands/TestWebhookCommand.cs +++ b/ProjectLighthouse/Administration/Maintenance/Commands/TestWebhookCommand.cs @@ -1,11 +1,12 @@ using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Logging; namespace LBPUnion.ProjectLighthouse.Administration.Maintenance.Commands; public class TestWebhookCommand : ICommand { - public async Task Run(string[] args) + public async Task Run(string[] args, Logger logger) { await WebhookHelper.SendWebhook("Testing 123", "Someone is testing the Discord webhook from the admin panel."); } diff --git a/ProjectLighthouse/Administration/Maintenance/Commands/WipeTokensForUserCommand.cs b/ProjectLighthouse/Administration/Maintenance/Commands/WipeTokensForUserCommand.cs index 68db13ae..9517bd56 100644 --- a/ProjectLighthouse/Administration/Maintenance/Commands/WipeTokensForUserCommand.cs +++ b/ProjectLighthouse/Administration/Maintenance/Commands/WipeTokensForUserCommand.cs @@ -2,6 +2,7 @@ using System; using System.Linq; using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using Microsoft.EntityFrameworkCore; @@ -19,7 +20,7 @@ public class WipeTokensForUserCommand : ICommand }; public string Arguments() => ""; public int RequiredArgs() => 1; - public async Task Run(string[] args) + public async Task Run(string[] args, Logger logger) { User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]); if (user == null) diff --git a/ProjectLighthouse/Administration/Maintenance/ICommand.cs b/ProjectLighthouse/Administration/Maintenance/ICommand.cs index 070e73e3..42a3f39f 100644 --- a/ProjectLighthouse/Administration/Maintenance/ICommand.cs +++ b/ProjectLighthouse/Administration/Maintenance/ICommand.cs @@ -1,14 +1,14 @@ using System.Threading.Tasks; using JetBrains.Annotations; +using LBPUnion.ProjectLighthouse.Logging; namespace LBPUnion.ProjectLighthouse.Administration.Maintenance; [UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)] public interface ICommand { - public string FirstAlias => this.Aliases()[0]; - public Task Run(string[] args); + public Task Run(string[] args, Logger logger); public string Name(); diff --git a/ProjectLighthouse/Administration/Maintenance/MaintenanceHelper.cs b/ProjectLighthouse/Administration/Maintenance/MaintenanceHelper.cs index 8e434bd8..3d86ad38 100644 --- a/ProjectLighthouse/Administration/Maintenance/MaintenanceHelper.cs +++ b/ProjectLighthouse/Administration/Maintenance/MaintenanceHelper.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.Logging.Loggers; namespace LBPUnion.ProjectLighthouse.Administration.Maintenance; @@ -28,7 +30,7 @@ public static class MaintenanceHelper .ToList()!; } - public static async Task RunCommand(string[] args) + public static async Task> RunCommand(string[] args) { if (args.Length < 1) throw new Exception @@ -36,18 +38,27 @@ public static class MaintenanceHelper string baseCmd = args[0]; args = args.Skip(1).ToArray(); + + // Setup memory logger for output + Logger logger = new(); + InMemoryLogger memoryLogger = new(); + logger.AddLogger(memoryLogger); IEnumerable suitableCommands = Commands.Where (command => command.Aliases().Any(a => a.ToLower() == baseCmd.ToLower())) .Where(command => args.Length >= command.RequiredArgs()); foreach (ICommand command in suitableCommands) { - Console.WriteLine("Running command " + command.Name()); - await command.Run(args); - return; + logger.LogInfo("Running command " + command.Name(), LogArea.Command); + + await command.Run(args, logger); + logger.Flush(); + return memoryLogger.Lines; } - Console.WriteLine("Command not found."); + logger.LogError("Command not found.", LogArea.Command); + logger.Flush(); + return memoryLogger.Lines; } public static async Task RunMaintenanceJob(string jobName) diff --git a/ProjectLighthouse/Extensions/LogLineListExtensions.cs b/ProjectLighthouse/Extensions/LogLineListExtensions.cs new file mode 100644 index 00000000..e198f0bc --- /dev/null +++ b/ProjectLighthouse/Extensions/LogLineListExtensions.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Linq; +using LBPUnion.ProjectLighthouse.Logging; + +namespace LBPUnion.ProjectLighthouse.Extensions; + +public static class LogLineListExtensions +{ + public static string ToLogString(this IEnumerable log) => log.Aggregate( + "", + (current, logLine) => current + $"[{logLine.Level}] [{logLine.Trace.Name}:{logLine.Trace.Section}] {logLine.Message}\n" + ); +} \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/CryptoHelper.cs b/ProjectLighthouse/Helpers/CryptoHelper.cs index 41c3d018..aef06419 100644 --- a/ProjectLighthouse/Helpers/CryptoHelper.cs +++ b/ProjectLighthouse/Helpers/CryptoHelper.cs @@ -71,6 +71,18 @@ public static class CryptoHelper return b; } + public static string ToBase64(string str) + { + byte[] bytes = Encoding.UTF8.GetBytes(str); + return Convert.ToBase64String(bytes); + } + + public static string FromBase64(string base64) + { + byte[] bytes = Convert.FromBase64String(base64); + return Encoding.UTF8.GetString(bytes); + } + #region Hash Functions public static string Sha256Hash(string str) => Sha256Hash(Encoding.UTF8.GetBytes(str)); @@ -86,5 +98,4 @@ public static class CryptoHelper public static string BCryptHash(byte[] bytes) => BCrypt.Net.BCrypt.HashPassword(Encoding.UTF8.GetString(bytes)); #endregion - } \ No newline at end of file diff --git a/ProjectLighthouse/Logging/LoggerBase.cs b/ProjectLighthouse/Logging/ILogger.cs similarity index 100% rename from ProjectLighthouse/Logging/LoggerBase.cs rename to ProjectLighthouse/Logging/ILogger.cs diff --git a/ProjectLighthouse/Logging/LogArea.cs b/ProjectLighthouse/Logging/LogArea.cs index ebb74275..dde0f5f4 100644 --- a/ProjectLighthouse/Logging/LogArea.cs +++ b/ProjectLighthouse/Logging/LogArea.cs @@ -19,4 +19,5 @@ public enum LogArea Resources, Logger, Redis, + Command, } \ No newline at end of file diff --git a/ProjectLighthouse/Logging/Logger.cs b/ProjectLighthouse/Logging/Logger.cs index 0078d464..183642fb 100644 --- a/ProjectLighthouse/Logging/Logger.cs +++ b/ProjectLighthouse/Logging/Logger.cs @@ -10,12 +10,6 @@ using System.Threading.Tasks; namespace LBPUnion.ProjectLighthouse.Logging; -// TODO: make into singleton, but with ability to also have instances -// Logger.LogSuccess() should still work and all, but ideally i'm also able to have another instance and do: -// Logger logger = new(); -// logger.LogSuccess(); -// I should also be able to access the log queue. -// This functionality is going to be used in the admin panel to get the output of commands. public class Logger { internal static readonly Logger Instance = new(); @@ -30,7 +24,7 @@ public class Logger public void AddLogger(ILogger logger) { loggers.Add(logger); - LogSuccess("Initialized " + logger.GetType().Name, LogArea.Logger); + LogDebug("Initialized " + logger.GetType().Name, LogArea.Logger); } private LogTrace getTrace(int extraTraceLines = 0) diff --git a/ProjectLighthouse/Logging/Loggers/InMemoryLogger.cs b/ProjectLighthouse/Logging/Loggers/InMemoryLogger.cs new file mode 100644 index 00000000..24413ec3 --- /dev/null +++ b/ProjectLighthouse/Logging/Loggers/InMemoryLogger.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace LBPUnion.ProjectLighthouse.Logging.Loggers; + +public class InMemoryLogger : ILogger +{ + public readonly List Lines = new(); + + public void Log(LogLine line) + { + lock(this.Lines) + { + this.Lines.Add(line); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/StartupTasks.cs b/ProjectLighthouse/StartupTasks.cs index 50165011..a9ba4ab5 100644 --- a/ProjectLighthouse/StartupTasks.cs +++ b/ProjectLighthouse/StartupTasks.cs @@ -1,7 +1,9 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using LBPUnion.ProjectLighthouse.Administration.Maintenance; using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Files; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; @@ -71,7 +73,8 @@ public static class StartupTasks if (args.Length != 0) { - MaintenanceHelper.RunCommand(args).Wait(); + List logLines = MaintenanceHelper.RunCommand(args).Result; + Console.WriteLine(logLines.ToLogString()); return; }