More development

This commit is contained in:
Eugene ;) 2020-05-02 18:57:42 +02:00
parent c1f5f82329
commit 29cde263d3
14 changed files with 181 additions and 43 deletions

View File

@ -6,18 +6,19 @@ namespace Seenginx.Models
{
public class ConfigFile
{
public string FullPath { get; set; }
public string Folder { get; set; }
public string Name { get; set; }
public bool HasDraft => !string.IsNullOrEmpty(DraftName);
public string DraftName { get; set; }
public string OriginalBody { get; set; }
public string Body { get; set; }
public string DraftBody { get; set; }
public string Permissions { get; set; }
public DateTime LastUpdated { get; set; }
public string[] Owners { get; set; }
public string Permissions { get; set; }
public bool CanBeDeleted { get; set; } = true;
public string IsVisible { get; set; } = string.Empty;
public string IsSelected { get; set; } = string.Empty;
public bool HasDraft => !string.IsNullOrEmpty(DraftName);
public bool CanBeDeleted { get; set; } = true;
public void Hide() { IsVisible = "is-hidden"; }
public void Unhide() { IsVisible = string.Empty; }

View File

@ -11,6 +11,12 @@ namespace Seenginx.Models
public Exception Exception { get; private set; } = null;
public D Data { get; private set; }
public Result() { }
public Result(D data)
{
Data = data;
}
public Result<D> Invalidate(string errorMessage, Exception exception = null)
{
AllOk = false;

View File

@ -1,15 +0,0 @@
using System;
namespace Seenginx.Models
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string Summary { get; set; }
}
}

View File

@ -12,7 +12,7 @@
</div>
<div class="control has-icons-left">
<div class="select is-small is-rounded">
<select class="neoInput" @onchange="e => OnFilterClick(e.Value.ToString())">
<select class="neoBtnSmall" @onchange="e => OnFilterClick(e.Value.ToString())">
@foreach (var filter in Filters)
{
<option value="@filter">@filter</option>
@ -37,7 +37,7 @@
@onchange="e => SelectedFile.DraftName = e.Value.ToString()">
</div>
<div class="control">
<button class="button is-small is-rounded neoBtn" @onclick="OnFileCloseClick">
<button class="button is-small is-rounded neoBtnSmall" @onclick="OnFileCloseClick">
<span class="icon is-medium">
<i class="mdi mdi-close"></i>
</span>
@ -75,13 +75,13 @@
<div class="filesActions">
<div class="buttons is-centered">
<button class="button is-rounded neoBtn is-small noBottomMargin" @onclick="OnAddDialog">
<button class="button is-rounded neoBtnSmall is-small noBottomMargin" @onclick="OnAddDialog">
<span class="icon is-small has-text-success">
<i class="mdi mdi-plus-box-outline"></i>
</span>
<span>Add</span>
</button>
<button class="button is-rounded neoBtn is-small noBottomMargin @IsSelectedFileDeletable" @onclick="OnDeleteDialog">
<button class="button is-rounded neoBtnSmall is-small noBottomMargin @IsSelectedFileDeletable" @onclick="OnDeleteDialog">
<span class="icon is-small has-text-danger">
<i class="mdi mdi-minus-box-outline"></i>
</span>
@ -94,10 +94,10 @@
@if (IsAnyFileSelected)
{
<div class="buttons is-centered">
<button @onclick="OnSaveDraft" class="button is-rounded neoBtn is-small has-icon-left noBottomMargin"><span class="icon is-left has-text-light"><i class="mdi mdi-content-save-outline"></i></span> <span>Save draft</span></button>
<button @onclick="OnUndoChanges" class="button is-rounded neoBtn is-small has-icon-left noBottomMargin"><span class="icon is-left has-text-dark"><i class="mdi mdi-undo-variant"></i></span> <span>Undo changes</span></button>
<button @onclick="OnSave" class="button is-rounded neoBtn is-small has-icon-left noBottomMargin"><span class="icon is-left has-text-success"><i class="mdi mdi-content-save-all-outline"></i></span> <span>Save</span></button>
<button @onclick="OnTest" class="button is-rounded neoBtn is-small has-icon-left noBottomMargin"><span class="icon is-left has-text-danger"><i class="mdi mdi-alert-outline"></i></span> <span>Test</span></button>
<button @onclick="OnSaveDraft" class="button is-rounded neoBtnSmall is-small has-icon-left noBottomMargin"><span class="icon is-left has-text-light"><i class="mdi mdi-content-save-outline"></i></span> <span>Save draft</span></button>
<button @onclick="OnUndoChanges" class="button is-rounded neoBtnSmall is-small has-icon-left noBottomMargin"><span class="icon is-left has-text-dark"><i class="mdi mdi-undo-variant"></i></span> <span>Undo changes</span></button>
<button @onclick="OnSave" class="button is-rounded neoBtnSmall is-small has-icon-left noBottomMargin"><span class="icon is-left has-text-success"><i class="mdi mdi-content-save-all-outline"></i></span> <span>Save</span></button>
<button @onclick="OnTest" class="button is-rounded neoBtnSmall is-small has-icon-left noBottomMargin"><span class="icon is-left has-text-danger"><i class="mdi mdi-alert-outline"></i></span> <span>Test</span></button>
</div>
}
else

View File

@ -29,7 +29,7 @@ namespace Seenginx.Components
[Parameter]
public EventCallback<CFile> UpdateFile { get; set; }
[Parameter]
public EventCallback<CFile> DeleteFile { get; set; }
public EventCallback DeleteFile { get; set; }
[Parameter]
public RenderFragment<CFile> Editor { get; set; } = null;
@ -134,7 +134,7 @@ namespace Seenginx.Components
{
Files.ForEach(f => f.Deselect());
file.Select();
await JsRuntime.InvokeVoidAsync("UpdateEditor", file.OriginalBody);
await JsRuntime.InvokeVoidAsync("UpdateEditor", file.Body);
await SelectedFileChanged.InvokeAsync(file);
}
@ -152,13 +152,13 @@ namespace Seenginx.Components
}
protected async Task OnUndoChanges(MouseEventArgs e)
{
SelectedFile.DraftBody = SelectedFile.OriginalBody;
await JsRuntime.InvokeVoidAsync("UpdateEditor", SelectedFile.OriginalBody);
SelectedFile.DraftBody = SelectedFile.Body;
await JsRuntime.InvokeVoidAsync("UpdateEditor", SelectedFile.Body);
}
protected async Task OnSave(MouseEventArgs e)
{
var draftCode = await JsRuntime.InvokeAsync<string>("GetEditorCode");
SelectedFile.OriginalBody = draftCode;
SelectedFile.Body = draftCode;
}
protected async Task OnTest(MouseEventArgs e)
{
@ -170,7 +170,7 @@ namespace Seenginx.Components
}
protected async Task OnDeleteDialog()
{
await UpdateFile.InvokeAsync(SelectedFile);
await DeleteFile.InvokeAsync(null);
}
}
}

View File

@ -1,10 +1,11 @@
@inherits NginxBase
@page "/nginx"
<p>ciao</p>
<FilesWithEditor CFile="ConfigFile" Filters="Filters" Files="ConfigFiles" FilterFolder="FilterFolder"
SelectedFile="SelectedFile" SelectedFileChanged="SelectedFileChanged"
AddFile="AddFile" UpdateFile="UpdateFile" DeleteFile="DeleteFile">
AddFile="AddFile" DeleteFile="DeleteFile">
</FilesWithEditor>
<Modal @ref="ModalRef">
<ModalBackdrop />

View File

@ -14,6 +14,8 @@ namespace Seenginx.Pages
{
[Inject]
public INginxService NginxService { get; set; }
[Inject]
public IFileManager FileService { get; set; }
public string InputSearch { get; set; }
@ -57,11 +59,17 @@ namespace Seenginx.Pages
{
ShowModal();
}
public async Task UpdateFile(ConfigFile configFile)
{
}
public async Task DeleteFile(ConfigFile configFile)
public async Task<Result<ConfigFile>> SaveDraftFileAsync() =>
await FileService.SaveUpdateDraftFileAsync(SelectedFile);
public async Task<Result<ConfigFile>> SaveFileAsync() =>
await FileService.SaveUpdateFileAsync(SelectedFile);
public Result<bool> DeleteFile(EventArgs eventArgs)
{
return FileService.DeleteFile(SelectedFile);
}
}
}

View File

@ -85,6 +85,26 @@
border: none !important;
transform: scale(1.1);
}
&Small {
box-shadow: -2px -2px 4px rgba($light-shadow, .5), 2px 2px 4px rgba($dark-shadow, .5);
background: none !important;
border: none !important;
transition: all .2s linear;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
&:focus {
box-shadow: -2px -2px 4px rgba($light-shadow, .5), 2px 2px 4px rgba($dark-shadow, .5) !important;
}
&:hover {
box-shadow: -4px -4px 8px rgba($light-shadow, .5), 4px 4px 8px rgba($dark-shadow, .5);
background: none !important;
border: none !important;
transform: scale(1.1);
}
}
}
&File {

View File

@ -0,0 +1,83 @@
using Seenginx.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Seenginx.Services
{
public class FileManager : IFileManager
{
public async Task<Result<ConfigFile>> SaveUpdateFileAsync(ConfigFile configFile)
{
var result = new Result<ConfigFile>(configFile);
try
{
var validationResult = ValidForUpdate(configFile);
if (!validationResult.AllOk)
return result.Invalidate($"Failed validation for: {validationResult.ErrorMessage}");
await File.WriteAllTextAsync(configFile.FullPath, configFile.Body);
return result;
}
catch (Exception ex)
{
return result.Invalidate($"Exception at {nameof(SaveUpdateFileAsync)}(), with {nameof(ConfigFile.Name)}=[{nameof(configFile.Name)}]", ex);
}
}
public async Task<Result<ConfigFile>> SaveUpdateDraftFileAsync(ConfigFile configFile)
{
var result = new Result<ConfigFile>(configFile);
try
{
var validationResult = ValidForUpdate(configFile);
if (!validationResult.AllOk)
return result.Invalidate($"Failed validation for: {validationResult.ErrorMessage}");
await File.WriteAllTextAsync($"{configFile.FullPath}.draft", configFile.DraftBody);
return result;
}
catch (Exception ex)
{
return result.Invalidate($"Exception at {nameof(SaveUpdateDraftFileAsync)}(), with {nameof(ConfigFile.Name)}=[{nameof(configFile.Name)}]", ex);
}
}
public Result<bool> DeleteFile(ConfigFile configFile)
{
var result = new Result<bool>();
try
{
var validationResult = ValidForUpdate(configFile);
if (!validationResult.AllOk)
return result.Invalidate($"Failed validation for: {validationResult.ErrorMessage}");
File.Delete(configFile.FullPath);
if (File.Exists($"{configFile.FullPath}.draft"))
File.Delete($"{configFile.FullPath}.draft");
return result;
}
catch (Exception ex)
{
return result.Invalidate($"Exception at {nameof(DeleteFile)}(), with {nameof(ConfigFile.Name)}=[{nameof(configFile.Name)}]", ex);
}
}
private Result<string> ValidForUpdate(ConfigFile configFile)
{
var result = new Result<string>();
if (!Directory.Exists(Directory.GetDirectoryRoot(configFile.FullPath)))
return result.Invalidate($"Directory '{Directory.GetDirectoryRoot(configFile.FullPath)}' doesn't exist.");
if (!File.Exists(configFile.FullPath))
return result.Invalidate($"File '{configFile.FullPath}' doesn't exist.");
return result;
}
}
}

View File

@ -0,0 +1,15 @@
using Seenginx.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Seenginx.Services
{
public interface IFileManager
{
Task<Result<ConfigFile>> SaveUpdateFileAsync(ConfigFile configFile);
Task<Result<ConfigFile>> SaveUpdateDraftFileAsync(ConfigFile configFile);
Result<bool> DeleteFile(ConfigFile configFile);
}
}

View File

@ -33,7 +33,8 @@ namespace Seenginx.Services
configFile.Folder = "/";
configFile.LastUpdated = File.GetLastWriteTime(fp);
configFile.Name = fileName;
configFile.OriginalBody = File.ReadAllText(fp);
configFile.FullPath = Path.Combine(ConfigPaths.NginxPath, configFile.Folder, configFile.Name);
configFile.Body = File.ReadAllText(fp);
return configFile;
});
var confdConfigFiles = confdConfigs.Select(fp =>
@ -44,7 +45,8 @@ namespace Seenginx.Services
configFile.Folder = "/conf.d";
configFile.LastUpdated = File.GetLastWriteTime(fp);
configFile.Name = fileName;
configFile.OriginalBody = File.ReadAllText(fp);
configFile.FullPath = Path.Combine(ConfigPaths.NginxPath, configFile.Folder, configFile.Name);
configFile.Body = File.ReadAllText(fp);
return configFile;
});
var sitesAvailableConfigFiles = sitesAvailableConfigs.Select(fp =>
@ -55,7 +57,8 @@ namespace Seenginx.Services
configFile.Folder = "/sites-available";
configFile.LastUpdated = File.GetLastWriteTime(fp);
configFile.Name = fileName;
configFile.OriginalBody = File.ReadAllText(fp);
configFile.FullPath = Path.Combine(ConfigPaths.NginxPath, configFile.Folder, configFile.Name);
configFile.Body = File.ReadAllText(fp);
return configFile;
});
var sitesEnabledConfigFiles = sitesEnabledConfigs.Select(fp =>
@ -66,7 +69,8 @@ namespace Seenginx.Services
configFile.Folder = "/sites-enabled";
configFile.LastUpdated = File.GetLastWriteTime(fp);
configFile.Name = fileName;
configFile.OriginalBody = File.ReadAllText(fp);
configFile.FullPath = Path.Combine(ConfigPaths.NginxPath, configFile.Folder, configFile.Name);
configFile.Body = File.ReadAllText(fp);
return configFile;
});
var finalList = new List<ConfigFile>();

View File

@ -48,6 +48,7 @@ namespace Seenginx
services.AddTransient<IDmesgService, DmesgService>();
services.AddTransient<INginxService, NginxService>();
services.AddTransient<ISystemDService, SystemDService>();
services.AddTransient<IFileManager, FileManager>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.

View File

@ -90,6 +90,20 @@ html {
background: none !important;
border: none !important;
transform: scale(1.1); }
.neoBtnSmall {
box-shadow: -2px -2px 4px rgba(251, 238, 208, 0.5), 2px 2px 4px rgba(241, 185, 65, 0.5);
background: none !important;
border: none !important;
transition: all .2s linear;
-webkit-backface-visibility: hidden;
backface-visibility: hidden; }
.neoBtnSmall:focus {
box-shadow: -2px -2px 4px rgba(251, 238, 208, 0.5), 2px 2px 4px rgba(241, 185, 65, 0.5) !important; }
.neoBtnSmall:hover {
box-shadow: -4px -4px 8px rgba(251, 238, 208, 0.5), 4px 4px 8px rgba(241, 185, 65, 0.5);
background: none !important;
border: none !important;
transform: scale(1.1); }
.neoFile {
box-shadow: 0px 0px 0px rgba(251, 238, 208, 0.5), 0px 0px 0px rgba(241, 185, 65, 0.5) !important;

File diff suppressed because one or more lines are too long