Merged logs view into edit page. WIP text editor for server files.
parent
1a84c4c51a
commit
c60cf157cd
|
@ -0,0 +1,31 @@
|
|||
namespace LANCommander.Extensions
|
||||
{
|
||||
public static class FileExtensions
|
||||
{
|
||||
public static bool IsBinaryFile(this FileInfo fileInfo, int checkLength = 8000, int requiredConsecutiveNul = 1)
|
||||
{
|
||||
const char nulChar = '\0';
|
||||
|
||||
int count = 0;
|
||||
|
||||
using (var reader = new StreamReader(fileInfo.FullName))
|
||||
{
|
||||
for (int i = 0; i < checkLength && !reader.EndOfStream; i++) {
|
||||
if ((char)reader.Read() == nulChar)
|
||||
{
|
||||
count++;
|
||||
|
||||
if (count >= requiredConsecutiveNul)
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
@page "/Servers/{id:guid}/Logs"
|
||||
@using Microsoft.AspNetCore.SignalR.Client
|
||||
@using Microsoft.AspNetCore.SignalR.Client
|
||||
@using NLog;
|
||||
@using XtermBlazor
|
||||
@attribute [Authorize]
|
||||
@inject ServerService ServerService
|
||||
@inject ServerProcessService ServerProcessService
|
||||
@inject NavigationManager NavigationManager
|
|
@ -0,0 +1,152 @@
|
|||
@using LANCommander.Extensions;
|
||||
|
||||
<GridRow>
|
||||
<GridCol Span="6">
|
||||
<Tree TItem="FileTreeNode"
|
||||
DataSource="FileTree"
|
||||
TitleExpression="x => x.DataItem.Name"
|
||||
ChildrenExpression="x => x.DataItem.Children"
|
||||
IsLeafExpression="x => !x.DataItem.HasChildren"
|
||||
OnDblClick="(args) => OpenFile(args.Node.DataItem)">
|
||||
</Tree>
|
||||
</GridCol>
|
||||
|
||||
<GridCol Span="18">
|
||||
<Breadcrumb>
|
||||
@foreach (var part in CurrentFile.Split(Path.DirectorySeparatorChar))
|
||||
{
|
||||
<BreadcrumbItem>@part</BreadcrumbItem>
|
||||
}
|
||||
</Breadcrumb>
|
||||
<StandaloneCodeEditor @ref="Editor" Id="editor" ConstructionOptions="EditorConstructionOptions" />
|
||||
</GridCol>
|
||||
</GridRow>
|
||||
|
||||
<style>
|
||||
.monaco-editor-container {
|
||||
min-height: 600px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@code {
|
||||
[Parameter] public string WorkingDirectory { get; set; }
|
||||
|
||||
StandaloneCodeEditor? Editor;
|
||||
|
||||
IEnumerable<string> Files;
|
||||
HashSet<FileTreeNode> FileTree { get; set; } = new HashSet<FileTreeNode>();
|
||||
string CurrentFile = "";
|
||||
|
||||
private StandaloneEditorConstructionOptions EditorConstructionOptions(StandaloneCodeEditor editor)
|
||||
{
|
||||
return new StandaloneEditorConstructionOptions
|
||||
{
|
||||
AutomaticLayout = true,
|
||||
Theme = "vs-dark"
|
||||
};
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
var root = new FileTreeNode
|
||||
{
|
||||
Name = "/",
|
||||
FullName = WorkingDirectory,
|
||||
IsExpanded = true
|
||||
};
|
||||
|
||||
root.PopulateChildren(WorkingDirectory);
|
||||
|
||||
FileTree.Add(root);
|
||||
}
|
||||
|
||||
private void OpenFile(FileTreeNode node)
|
||||
{
|
||||
if (File.Exists(node.FullName))
|
||||
{
|
||||
var file = new FileInfo(node.FullName);
|
||||
|
||||
CurrentFile = node.FullName;
|
||||
|
||||
if (!file.IsBinaryFile())
|
||||
{
|
||||
Editor.SetValue(File.ReadAllText(file.FullName));
|
||||
}
|
||||
else
|
||||
{
|
||||
Editor.SetValue("This file cannot be edited because it is a binary file.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class FileTreeNode
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string FullName { get; set; }
|
||||
public bool IsExpanded { get; set; } = false;
|
||||
public bool IsDirectory { get; set; } = false;
|
||||
public bool HasChildren => Children != null && Children.Count > 0;
|
||||
public HashSet<FileTreeNode> Children { get; set; } = new HashSet<FileTreeNode>();
|
||||
|
||||
public void PopulateChildren(string path)
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var file in Directory.GetFiles(path))
|
||||
{
|
||||
var fileInfo = new FileInfo(file);
|
||||
|
||||
Children.Add(new FileTreeNode
|
||||
{
|
||||
Name = fileInfo.Name,
|
||||
FullName = fileInfo.FullName,
|
||||
IsDirectory = false
|
||||
});
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var directory in Directory.GetDirectories(path))
|
||||
{
|
||||
var directoryInfo = new DirectoryInfo(directory);
|
||||
|
||||
var child = new FileTreeNode
|
||||
{
|
||||
Name = directoryInfo.Name,
|
||||
FullName = directoryInfo.FullName,
|
||||
IsDirectory = true
|
||||
};
|
||||
|
||||
child.PopulateChildren(directoryInfo.FullName);
|
||||
|
||||
Children.Add(child);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
public void PopulateChildren(IEnumerable<string> paths)
|
||||
{
|
||||
var childPaths = paths.Where(p => p.StartsWith(FullName) && p.EndsWith(Path.PathSeparator));
|
||||
var directChildren = childPaths.Where(p => p != FullName && p.Substring(FullName.Length + 1).TrimEnd(Path.PathSeparator).Split(Path.PathSeparator).Length == 1);
|
||||
|
||||
foreach (var directChild in directChildren)
|
||||
{
|
||||
var child = new FileTreeNode()
|
||||
{
|
||||
FullName = directChild,
|
||||
Name = directChild.Substring(FullName.Length).TrimEnd(Path.PathSeparator)
|
||||
};
|
||||
|
||||
child.PopulateChildren(paths);
|
||||
|
||||
Children.Add(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,79 @@
|
|||
@page "/Servers/{id:guid}/Edit"
|
||||
@page "/Servers/{id:guid}/Edit/{panel}"
|
||||
@page "/Servers/Add"
|
||||
@using LANCommander.Pages.Servers.Components
|
||||
@inject ServerService ServerService
|
||||
@inject ServerProcessService ServerProcessService
|
||||
@inject IMessageService MessageService
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<Card Title="Server Details">
|
||||
<Body>
|
||||
<Layout Class="site-layout-background" Style="padding: 24px 0;">
|
||||
<Sider Class="site-layout-background" Width="200">
|
||||
<Menu Mode="@MenuMode.Inline" Style="height: 100%;">
|
||||
<MenuItem RouterLink="@($"/Servers/{Server.Id}/Edit/General")">General</MenuItem>
|
||||
|
||||
@if (Server != null && Server.Id != Guid.Empty)
|
||||
{
|
||||
<MenuItem RouterLink="@($"/Servers/{Server.Id}/Edit/Logs")">Logs</MenuItem>
|
||||
<MenuItem RouterLink="@($"/Servers/{Server.Id}/Edit/Files")">Files</MenuItem>
|
||||
}
|
||||
</Menu>
|
||||
</Sider>
|
||||
|
||||
<Content>
|
||||
@if (Panel == "Logs")
|
||||
{
|
||||
<PageHeader>
|
||||
<PageHeaderTitle>Logs</PageHeaderTitle>
|
||||
<PageHeaderExtra>
|
||||
<Space Size="@("large")">
|
||||
<SpaceItem Style="margin-right: 24px;">
|
||||
@switch (Status)
|
||||
{
|
||||
case ServerProcessStatus.Running:
|
||||
<Badge Status="success" Text="Running" />
|
||||
break;
|
||||
|
||||
case ServerProcessStatus.Starting:
|
||||
<Badge Status="processing" Text="Starting" />
|
||||
break;
|
||||
|
||||
case ServerProcessStatus.Error:
|
||||
<Badge Status="error" Text="Error" />
|
||||
break;
|
||||
|
||||
case ServerProcessStatus.Stopped:
|
||||
default:
|
||||
<Badge Status="default" Text="Stopped" />
|
||||
break;
|
||||
}
|
||||
</SpaceItem>
|
||||
|
||||
@if (Status == ServerProcessStatus.Error || Status == ServerProcessStatus.Stopped)
|
||||
{
|
||||
<Button Type="@ButtonType.Primary" OnClick="() => Start()">Start</Button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<Popconfirm OnConfirm="() => Stop()" Title="Are you sure you want to kill this server process?">
|
||||
<Button Danger Type="@ButtonType.Primary">Stop</Button>
|
||||
</Popconfirm>
|
||||
}
|
||||
</Space>
|
||||
</PageHeaderExtra>
|
||||
</PageHeader>
|
||||
}
|
||||
else
|
||||
{
|
||||
<PageHeader>
|
||||
<PageHeaderTitle>@Panel</PageHeaderTitle>
|
||||
</PageHeader>
|
||||
}
|
||||
|
||||
|
||||
<div class="site-layout-content">
|
||||
@if (Panel == "General")
|
||||
{
|
||||
<Form Model="@Server" Layout="@FormLayout.Vertical">
|
||||
<FormItem Label="Name">
|
||||
<Input @bind-Value="@context.Name" />
|
||||
|
@ -23,13 +91,42 @@
|
|||
<Button Type="@ButtonType.Primary" OnClick="Save" Icon="@IconType.Fill.Save">Save</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Body>
|
||||
</Card>
|
||||
}
|
||||
|
||||
@if (Panel == "Logs")
|
||||
{
|
||||
<Logs Id="@Server.Id" />
|
||||
}
|
||||
|
||||
@if (Panel == "Files")
|
||||
{
|
||||
<TextEditor WorkingDirectory="@Server.WorkingDirectory" />
|
||||
}
|
||||
</div>
|
||||
</Content>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
.site-layout-background {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.site-layout-content {
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.ant-layout-content > .ant-page-header-heading {
|
||||
padding-top: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@code {
|
||||
[Parameter] public Guid Id { get; set; }
|
||||
[Parameter] public string Panel { get; set; }
|
||||
|
||||
Server Server;
|
||||
ServerProcessStatus Status;
|
||||
Timer Timer;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
|
@ -37,6 +134,32 @@
|
|||
Server = new Server();
|
||||
else
|
||||
Server = await ServerService.Get(Id);
|
||||
|
||||
if (String.IsNullOrWhiteSpace(Server.WorkingDirectory))
|
||||
Server.WorkingDirectory = "C:\\";
|
||||
}
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
Timer = new Timer(async (object? stateInfo) =>
|
||||
{
|
||||
Status = ServerProcessService.GetStatus(Server);
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}, new AutoResetEvent(false), 1000, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
ServerProcessService.StartServer(Server);
|
||||
}
|
||||
|
||||
private void Stop()
|
||||
{
|
||||
ServerProcessService.StopServer(Server);
|
||||
}
|
||||
|
||||
private async Task Save()
|
||||
|
|
|
@ -105,12 +105,12 @@
|
|||
|
||||
private void Edit(Server server)
|
||||
{
|
||||
NavigationManager.NavigateTo($"/Servers/{server.Id}/Edit");
|
||||
NavigationManager.NavigateTo($"/Servers/{server.Id}/Edit/General");
|
||||
}
|
||||
|
||||
private void Logs(Server server)
|
||||
{
|
||||
NavigationManager.NavigateTo($"/Servers/{server.Id}/Logs");
|
||||
NavigationManager.NavigateTo($"/Servers/{server.Id}/Edit/Logs");
|
||||
}
|
||||
|
||||
private void Start(Server server)
|
||||
|
|
Loading…
Reference in New Issue