Add command output to admin panel

This commit is contained in:
jvyden 2022-05-15 17:23:43 -04:00
parent 630b38e7bb
commit a733eb21a4
No known key found for this signature in database
GPG key ID: 18BCF2BE0262B278
18 changed files with 114 additions and 37 deletions

View file

@ -11,6 +11,17 @@
Model.Title = "Admin Panel";
}
@if (Model.Log != null)
{
<div class="ui bottom attached message">
<h2>Command Output</h2>
@foreach (string line in Model.Log.Split("\n"))
{
<code>@line.TrimEnd()</code><br>
}
</div>
}
@if (!this.Request.IsMobile())
{
<div class="ui center aligned grid">

View file

@ -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<AdminPanelStatistic> Statistics = new();
public async Task<IActionResult> OnGet([FromQuery] string? args, [FromQuery] string? command, [FromQuery] string? maintenanceJob)
public string? Log;
public async Task<IActionResult> 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<LogLine> 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();
}
}

View file

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

View file

@ -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() => "<username/userId>";
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;
}

View file

@ -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() => "<username/userId>";
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);
}
}

View file

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

View file

@ -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() => "<username/userId> <sha256/plaintext>";
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);
}
}

View file

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

View file

@ -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() => "<username/userId>";
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)

View file

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

View file

@ -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<List<LogLine>> 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<ICommand> 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)

View file

@ -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<LogLine> log) => log.Aggregate(
"",
(current, logLine) => current + $"[{logLine.Level}] [{logLine.Trace.Name}:{logLine.Trace.Section}] {logLine.Message}\n"
);
}

View file

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

View file

@ -19,4 +19,5 @@ public enum LogArea
Resources,
Logger,
Redis,
Command,
}

View file

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

View file

@ -0,0 +1,16 @@
using System.Collections.Generic;
namespace LBPUnion.ProjectLighthouse.Logging.Loggers;
public class InMemoryLogger : ILogger
{
public readonly List<LogLine> Lines = new();
public void Log(LogLine line)
{
lock(this.Lines)
{
this.Lines.Add(line);
}
}
}

View file

@ -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<LogLine> logLines = MaintenanceHelper.RunCommand(args).Result;
Console.WriteLine(logLines.ToLogString());
return;
}