Continuing building
This commit is contained in:
parent
73a6125faf
commit
20a4a9ebbf
@ -27,4 +27,21 @@ namespace Seenginx.Models
|
||||
|
||||
public void SetData(D data) => Data = data;
|
||||
}
|
||||
|
||||
public class Result
|
||||
{
|
||||
public bool AllOk { get; private set; } = true;
|
||||
public string ErrorMessage { get; private set; }
|
||||
public Exception Exception { get; private set; } = null;
|
||||
|
||||
public Result Invalidate(string errorMessage, Exception exception = null)
|
||||
{
|
||||
AllOk = false;
|
||||
ErrorMessage = errorMessage;
|
||||
if (exception != null)
|
||||
Exception = exception;
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
<div class="control has-icons-left is-expanded">
|
||||
<input formnovalidate @oninput="e => SearchInputChanged(e.Value.ToString())" @bind-value="SearchInput" class="input is-rounded is-small neoInput" type="text" placeholder="Search...">
|
||||
<span class="icon is-small is-left has-text-dark">
|
||||
<i class="mdi mdi-file-search-outline"></i>
|
||||
<i class="mdi mdi-file-search"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -17,33 +17,39 @@
|
||||
@if (IsAnyFileSelected)
|
||||
{
|
||||
<div class="field is-grouped">
|
||||
<div class="control is-expanded borderRBig neoInput has-text-centered has-icons-left">
|
||||
<input class="input is-5 is-rounded is-small neoInput" type="text" placeholder="Search..."
|
||||
disabled="@(SelectedFile.CanBeDeleted ? null : "disabled")" @bind-value="SelectedFile.Name">
|
||||
<span class="icon is-small is-left @(ConfigHasChanged ? "has-text-danger" : "has-text-dark")">
|
||||
<i class="mdi @(ConfigHasChanged ? "mdi-not-equal" : "mdi-equal")"></i>
|
||||
</span>
|
||||
<div class="control is-expanded borderRBig neomorphXSmall has-icons-left has-text-centered">
|
||||
<span>@SelectedFile.Name</span>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-small is-rounded neoBtnSmall" @onclick="OnFileRenameClick">
|
||||
<p class="control">
|
||||
<button class="button is-small is-rounded neoBtnSmall" @onclick="OnFileRenameClick" title="Rename configuration">
|
||||
<span class="icon is-medium">
|
||||
<i class="mdi mdi-pencil-outline"></i>
|
||||
<i class="mdi mdi-pencil"></i>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-small is-rounded neoBtnSmall" @onclick="OnFileCloseClick">
|
||||
</p>
|
||||
@if (!string.IsNullOrEmpty(SelectedFile.DraftBody))
|
||||
{
|
||||
<p class="control">
|
||||
<button class="button is-small is-rounded neoBtnSmall" @onclick="OnLoadDraftClick" title="Load draft">
|
||||
<span class="icon is-medium">
|
||||
<i class="mdi mdi-content-save-edit"></i>
|
||||
</span>
|
||||
</button>
|
||||
</p>
|
||||
}
|
||||
<p class="control">
|
||||
<button class="button is-small is-rounded neoBtnSmall" @onclick="OnFileCloseClick" title="Close">
|
||||
<span class="icon is-medium">
|
||||
<i class="mdi mdi-close"></i>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="field">
|
||||
<div class="control is-expanded borderRBig neoInput has-text-centered"><span>···</span></div>
|
||||
<div class="control is-expanded borderRBig neomorphXSmall has-text-centered"><span>···</span></div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@ -52,8 +58,8 @@
|
||||
<div class="filesList">
|
||||
@foreach (var file in Files)
|
||||
{
|
||||
<div @onclick="async e => await OnFileClick(e,file)" title="@(string.IsNullOrEmpty(file.Folder) ? $"/{file.Name}" : $"/{file.Folder}/{file.Name}")" @key="file" class="confFile borderRSmall isFinger neoFile @file.IsVisible @file.IsSelected">
|
||||
<p class="has-text-weight-bold ellipsis"><span class="icon @(file.CanBeDeleted ? null : "has-text-danger")"><i class="mdi mdi-file-cog-outline"></i></span> @file.Name</p>
|
||||
<div @onclick="async e => await OnFileClick(e,file)" title="@file.FullPath" @key="file" class="confFile borderRSmall isFinger neoFile @file.IsVisible @file.IsSelected">
|
||||
<p class="has-text-weight-bold ellipsis"><span class="icon @(file.CanBeDeleted ? null : "has-text-danger")"><i class="mdi mdi-file-cog"></i></span> @file.Name</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@ -91,10 +97,10 @@
|
||||
<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>
|
||||
@if (HasTesting)
|
||||
{
|
||||
<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>
|
||||
<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"></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>@(string.IsNullOrEmpty(SelectedFile.DraftBody) ? "Create" : "Save") draft</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="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"></i></span> <span>@(string.IsNullOrEmpty(SelectedFile.DraftBody) ? "Create" : "Save") draft</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"></i></span> <span>Save</span></button>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
|
@ -33,7 +33,6 @@ namespace Seenginx.Components
|
||||
[Parameter] public CFile SelectedFile { get; set; } = default;
|
||||
|
||||
protected string SearchInput { get; set; }
|
||||
private bool ConfigHasChanged => (string.IsNullOrEmpty(SelectedFile.DraftBody) ? false : SelectedFile.Body != SelectedFile.DraftBody);
|
||||
|
||||
protected bool IsAnyFileSelected => SelectedFile != default;
|
||||
protected string IsSelectedFileDeletable
|
||||
@ -90,6 +89,11 @@ namespace Seenginx.Components
|
||||
await RenameFileCallback.InvokeAsync(null);
|
||||
}
|
||||
|
||||
protected async Task OnLoadDraftClick(MouseEventArgs e)
|
||||
{
|
||||
await JsRuntime.InvokeVoidAsync("UpdateEditor", SelectedFile.DraftBody);
|
||||
}
|
||||
|
||||
protected async Task OnFileCloseClick(MouseEventArgs e)
|
||||
{
|
||||
Files.ForEach(f => f.Deselect());
|
||||
|
@ -1,7 +1,8 @@
|
||||
@inherits NginxBase
|
||||
@page "/nginx"
|
||||
<FilesWithEditor CFile="ConfigFile" Files="ConfigFiles" SelectedFile="SelectedFile" SelectedFileChanged="SelectedFileChanged"
|
||||
TestConfiguration="TestConfiguration" DeleteFileCallback="DeleteFile" ShowAddFileModal="ShowAddFileModal"
|
||||
TestConfiguration="TestConfiguration" DeleteFileCallback="DeleteFile" ShowAddFileModal="AddFileModal"
|
||||
RenameFileCallback="RenameFileModal"
|
||||
HasTesting="true" SaveFileCallback="SaveFileAsync" SaveDraftFileCallback="SaveDraftFileAsync">
|
||||
</FilesWithEditor>
|
||||
|
||||
|
@ -17,7 +17,6 @@ namespace Seenginx.Pages
|
||||
public class NginxBase : ComponentBase
|
||||
{
|
||||
[Inject] public INginxService NginxService { get; set; }
|
||||
[Inject] public IFileManager FileService { get; set; }
|
||||
[Inject] public IModalService Modal { get; set; }
|
||||
|
||||
public string InputSearch { get; set; }
|
||||
@ -47,7 +46,7 @@ namespace Seenginx.Pages
|
||||
SelectedFile = configFile;
|
||||
}
|
||||
|
||||
public async Task ShowAddFileModal()
|
||||
public async Task AddFileModal()
|
||||
{
|
||||
var parameters = new ModalParameters();
|
||||
parameters.Add(nameof(Templates), Templates);
|
||||
@ -57,7 +56,7 @@ namespace Seenginx.Pages
|
||||
if (result.Cancelled) return;
|
||||
|
||||
|
||||
var validationResult = await NginxService.ValidateNewConfigurationAsync((NewFileForm)result.Data);
|
||||
var validationResult = await NginxService.ValidateForAddFileAsync((NewFileForm)result.Data);
|
||||
|
||||
if (!validationResult.AllOk)
|
||||
{
|
||||
@ -77,14 +76,70 @@ namespace Seenginx.Pages
|
||||
SelectedFile.Select();
|
||||
}
|
||||
|
||||
public async Task RenameFileModal()
|
||||
{
|
||||
var parameters = new ModalParameters();
|
||||
parameters.Add(nameof(RenameForm.Name), SelectedFile.Name);
|
||||
|
||||
var resultAwait = Modal.Show<RenameForm>(string.Empty, parameters);
|
||||
var result = await resultAwait.Result;
|
||||
if (result.Cancelled) return;
|
||||
|
||||
var validationResult = await NginxService.ValidateForRenameFileAsync(ConfigFiles, SelectedFile, result.Data.ToString());
|
||||
if (!validationResult.AllOk)
|
||||
{
|
||||
var popupAwait = Modal.Show<GenericPopup>(string.Empty, new ModalParameters().Setup(PopupType.Ok, validationResult.ErrorMessage));
|
||||
await popupAwait.Result;
|
||||
return;
|
||||
}
|
||||
|
||||
var renameResult = await NginxService.RenameFileAsync(SelectedFile, $"{result.Data}.conf");
|
||||
if (!renameResult.AllOk)
|
||||
{
|
||||
var popupAwait = Modal.Show<GenericPopup>(string.Empty, new ModalParameters().Setup(PopupType.Ok, renameResult.ErrorMessage));
|
||||
await popupAwait.Result;
|
||||
return;
|
||||
}
|
||||
|
||||
SelectedFile.Name = result.Data.ToString();
|
||||
}
|
||||
|
||||
public async Task SaveFileAsync()
|
||||
{
|
||||
var saveUpdateResult = await FileService.SaveFileAsync(SelectedFile);
|
||||
var validationResult = await NginxService.ValidateForSaveFileAsync(SelectedFile);
|
||||
|
||||
if (!validationResult.AllOk)
|
||||
{
|
||||
await Modal.Show<GenericPopup>(string.Empty, new ModalParameters().Setup(PopupType.Ok, validationResult.ErrorMessage)).Result;
|
||||
return;
|
||||
}
|
||||
|
||||
var saveResult = await NginxService.SaveFileAsync(SelectedFile);
|
||||
|
||||
if (!saveResult.AllOk)
|
||||
{
|
||||
await Modal.Show<GenericPopup>(string.Empty, new ModalParameters().Setup(PopupType.Ok, saveResult.ErrorMessage)).Result;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SaveDraftFileAsync()
|
||||
{
|
||||
var saveUpdateDraftResult = await FileService.SaveDraftFileAsync(SelectedFile);
|
||||
var validationResult = await NginxService.ValidateForSaveDraftFileAsync(SelectedFile);
|
||||
|
||||
if (!validationResult.AllOk)
|
||||
{
|
||||
await Modal.Show<GenericPopup>(string.Empty, new ModalParameters().Setup(PopupType.Ok, validationResult.ErrorMessage)).Result;
|
||||
return;
|
||||
}
|
||||
|
||||
var saveDraftResult = await NginxService.SaveDraftFileAsync(SelectedFile);
|
||||
|
||||
if (!saveDraftResult.AllOk)
|
||||
{
|
||||
await Modal.Show<GenericPopup>(string.Empty, new ModalParameters().Setup(PopupType.Ok, saveDraftResult.ErrorMessage)).Result;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task TestConfiguration()
|
||||
|
@ -104,6 +104,12 @@
|
||||
border: none !important;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
&Plain {
|
||||
box-shadow: -2px -2px 4px rgba($light-shadow, .5), 2px 2px 4px rgba($dark-shadow, .5);
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,83 +0,0 @@
|
||||
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>> SaveFileAsync(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(SaveFileAsync)}(), with {nameof(ConfigFile.Name)}=[{nameof(configFile.Name)}]", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result<ConfigFile>> SaveDraftFileAsync(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(SaveDraftFileAsync)}(), 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
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>> SaveFileAsync(ConfigFile configFile);
|
||||
Task<Result<ConfigFile>> SaveDraftFileAsync(ConfigFile configFile);
|
||||
Result<bool> DeleteFile(ConfigFile configFile);
|
||||
}
|
||||
}
|
@ -8,12 +8,22 @@ namespace Seenginx.Services
|
||||
{
|
||||
Task<IEnumerable<ConfigFile>> GetFilesAsync();
|
||||
Task<IEnumerable<Template>> GetTemplates();
|
||||
|
||||
Task<Result<string>> TestFileAsync(ConfigFile configFile);
|
||||
|
||||
Task<Result> ValidateForAddFileAsync(NewFileForm newFileForm);
|
||||
Task<Result<ConfigFile>> AddFileAsync(NewFileForm newFileForm);
|
||||
|
||||
Task<Result> ValidateForSaveFileAsync(ConfigFile configFile);
|
||||
Task<Result<ConfigFile>> SaveFileAsync(ConfigFile configFile);
|
||||
|
||||
Task<Result> ValidateForSaveDraftFileAsync(ConfigFile configFile);
|
||||
Task<Result<ConfigFile>> SaveDraftFileAsync(ConfigFile configFile);
|
||||
Task<Result<bool>> RenameFileAsync(ConfigFile configFile);
|
||||
Task<Result<bool>> DeleteFileAsync(ConfigFile configFile);
|
||||
Task<Result<bool>> ValidateNewConfigurationAsync(NewFileForm newFileForm);
|
||||
|
||||
Task<Result> ValidateForRenameFileAsync(List<ConfigFile> configFiles, ConfigFile configFile, string newName);
|
||||
Task<Result> RenameFileAsync(ConfigFile configFile, string newName);
|
||||
|
||||
Task<Result> ValidateForDeleteFileAsync(ConfigFile configFile);
|
||||
Task<Result> DeleteFileAsync(ConfigFile configFile);
|
||||
}
|
||||
}
|
@ -20,110 +20,6 @@ namespace Seenginx.Services
|
||||
ConfigPaths = configPaths;
|
||||
}
|
||||
|
||||
public async Task<Result<bool>> ValidateNewConfigurationAsync(NewFileForm newFileForm)
|
||||
{
|
||||
var validationResult = new Result<bool>();
|
||||
try
|
||||
{
|
||||
var filePath = Path.Combine(ConfigPaths.NginxPath, "conf.d", $"{newFileForm.Name}.conf");
|
||||
|
||||
if (File.Exists(filePath))
|
||||
return validationResult.Invalidate($"There's already a file with the '{newFileForm.Name}.conf' name.");
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return validationResult.Invalidate($"Exception at {nameof(ValidateNewConfigurationAsync)}()", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result<ConfigFile>> AddFileAsync(NewFileForm newFileForm)
|
||||
{
|
||||
var addResult = new Result<ConfigFile>();
|
||||
try
|
||||
{
|
||||
var newFile = new ConfigFile();
|
||||
newFile.Name = $"{newFileForm.Name}.conf";
|
||||
newFile.Folder = "/conf.d";
|
||||
newFile.FullPath = Path.Combine(ConfigPaths.NginxPath, "conf.d", newFile.Name);
|
||||
newFile.Body = newFileForm.SelectedTemplate == 0.ToString() ? string.Empty : (await GetTemplates()).SingleOrDefault(t => t.Name == newFileForm.SelectedTemplate)?.Code;
|
||||
newFile.LastUpdated = DateTime.UtcNow;
|
||||
newFile.DraftBody = newFile.Body;
|
||||
|
||||
await File.WriteAllTextAsync(newFile.FullPath, newFile.Body, Encoding.UTF8);
|
||||
|
||||
addResult.SetData(newFile);
|
||||
|
||||
return addResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return addResult.Invalidate(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result<bool>> DeleteFileAsync(ConfigFile configFile)
|
||||
{
|
||||
var result = new Result<bool>(true);
|
||||
try
|
||||
{
|
||||
if (!File.Exists(configFile.FullPath))
|
||||
return result.Invalidate($"File '{configFile.FullPath}' not found.");
|
||||
|
||||
File.Delete(configFile.FullPath);
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return result.Invalidate(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result<string>> TestFileAsync(ConfigFile configFile)
|
||||
{
|
||||
await Task.Run(() => { });
|
||||
var result = new Result<string>();
|
||||
result.SetData("Uhu");
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Result<ConfigFile>> SaveFileAsync(ConfigFile configFile)
|
||||
{
|
||||
var saveResult = new Result<ConfigFile>();
|
||||
try
|
||||
{
|
||||
await File.WriteAllTextAsync(Path.Combine(ConfigPaths.NginxPath, configFile.Folder, configFile.Name), configFile.Body, Encoding.UTF8);
|
||||
|
||||
return saveResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return saveResult.Invalidate(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result<ConfigFile>> SaveDraftFileAsync(ConfigFile configFile)
|
||||
{
|
||||
var saveDraftResult = new Result<ConfigFile>();
|
||||
try
|
||||
{
|
||||
await File.WriteAllTextAsync(Path.Combine(ConfigPaths.NginxPath, configFile.Folder, $"{configFile.Name}.draft"), configFile.DraftBody, Encoding.UTF8);
|
||||
|
||||
return saveDraftResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return saveDraftResult.Invalidate(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result<bool>> RenameFileAsync(ConfigFile configFile, string newName)
|
||||
{
|
||||
File.Move()
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ConfigFile>> GetFilesAsync()
|
||||
{
|
||||
await Task.Run(() => { });
|
||||
@ -138,28 +34,28 @@ namespace Seenginx.Services
|
||||
|
||||
var rootConfigFiles = rootConfigs.Select(fp =>
|
||||
{
|
||||
var fileName = Path.GetFileName(fp);
|
||||
var name = Path.GetFileNameWithoutExtension(fp);
|
||||
var configFile = new ConfigFile();
|
||||
configFile.CanBeDeleted = false;
|
||||
configFile.Folder = string.Empty;
|
||||
configFile.LastUpdated = File.GetLastWriteTime(fp);
|
||||
configFile.Name = fileName;
|
||||
configFile.FullPath = Path.Combine(ConfigPaths.NginxPath, configFile.Name);
|
||||
configFile.Name = name;
|
||||
configFile.FullPath = Path.Combine(ConfigPaths.NginxPath, $"{configFile.Name}.conf");
|
||||
configFile.Body = File.ReadAllText(fp);
|
||||
if (rootDraftConfigs.Any(cfp => cfp.Contains(fileName)))
|
||||
configFile.DraftBody = File.ReadAllText(rootDraftConfigs.First(cfp => cfp.Contains(fileName)));
|
||||
if (rootDraftConfigs.Any(cfp => cfp.Contains(name)))
|
||||
configFile.DraftBody = File.ReadAllText(rootDraftConfigs.First(cfp => cfp.Contains(name)));
|
||||
|
||||
return configFile;
|
||||
});
|
||||
var confdConfigFiles = confdConfigs.Select(fp =>
|
||||
{
|
||||
var fileName = Path.GetFileName(fp);
|
||||
var fileName = Path.GetFileNameWithoutExtension(fp);
|
||||
var configFile = new ConfigFile();
|
||||
configFile.CanBeDeleted = true;
|
||||
configFile.Folder = "conf.d";
|
||||
configFile.LastUpdated = File.GetLastWriteTime(fp);
|
||||
configFile.Name = fileName;
|
||||
configFile.FullPath = Path.Combine(ConfigPaths.NginxPath, configFile.Folder, configFile.Name);
|
||||
configFile.FullPath = Path.Combine(ConfigPaths.NginxPath, configFile.Folder, $"{configFile.Name}.conf");
|
||||
configFile.Body = File.ReadAllText(fp);
|
||||
if (confDraftConfigs.Any(cfp => cfp.Contains(fileName)))
|
||||
configFile.DraftBody = File.ReadAllText(confDraftConfigs.First(cfp => cfp.Contains(fileName)));
|
||||
@ -168,13 +64,13 @@ namespace Seenginx.Services
|
||||
});
|
||||
var sitesAvailableConfigFiles = sitesAvailableConfigs.Select(fp =>
|
||||
{
|
||||
var fileName = Path.GetFileName(fp);
|
||||
var fileName = Path.GetFileNameWithoutExtension(fp);
|
||||
var configFile = new ConfigFile();
|
||||
configFile.CanBeDeleted = true;
|
||||
configFile.Folder = "sites-available";
|
||||
configFile.LastUpdated = File.GetLastWriteTime(fp);
|
||||
configFile.Name = fileName;
|
||||
configFile.FullPath = Path.Combine(ConfigPaths.NginxPath, configFile.Folder, configFile.Name);
|
||||
configFile.FullPath = Path.Combine(ConfigPaths.NginxPath, configFile.Folder, $"{configFile.Name}.conf");
|
||||
configFile.Body = File.ReadAllText(fp);
|
||||
if (sitesAvailableDraftConfigs.Any(cfp => cfp.Contains(fileName)))
|
||||
configFile.DraftBody = File.ReadAllText(sitesAvailableDraftConfigs.First(cfp => cfp.Contains(fileName)));
|
||||
@ -237,11 +133,209 @@ namespace Seenginx.Services
|
||||
}
|
||||
|
||||
|
||||
public async Task<Result> ValidateForAddFileAsync(NewFileForm newFileForm)
|
||||
{
|
||||
var validationResult = new Result();
|
||||
try
|
||||
{
|
||||
var filePath = Path.Combine(ConfigPaths.NginxPath, "conf.d", $"{newFileForm.Name}.conf");
|
||||
|
||||
if (File.Exists(filePath))
|
||||
return validationResult.Invalidate($"There's already a file with the '{newFileForm.Name}.conf' name.");
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return validationResult.Invalidate($"Exception at {nameof(ValidateForAddFileAsync)}()", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result<ConfigFile>> AddFileAsync(NewFileForm newFileForm)
|
||||
{
|
||||
var addResult = new Result<ConfigFile>();
|
||||
try
|
||||
{
|
||||
var newFile = new ConfigFile();
|
||||
newFile.Name = $"{newFileForm.Name}.conf";
|
||||
newFile.Folder = "conf.d";
|
||||
newFile.FullPath = Path.Combine(ConfigPaths.NginxPath, newFile.Folder, newFile.Name);
|
||||
newFile.Body = newFileForm.SelectedTemplate == 0.ToString() ? string.Empty : (await GetTemplates()).SingleOrDefault(t => t.Name == newFileForm.SelectedTemplate)?.Code;
|
||||
newFile.LastUpdated = DateTime.UtcNow;
|
||||
|
||||
await File.WriteAllTextAsync(newFile.FullPath, newFile.Body, Encoding.UTF8);
|
||||
|
||||
addResult.SetData(newFile);
|
||||
|
||||
return addResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return addResult.Invalidate(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//public async Task<Result<bool>> DeleteFileAsync(ConfigFile configFile)
|
||||
public async Task<Result> ValidateForDeleteFileAsync(ConfigFile configFile)
|
||||
{
|
||||
var validationResult = new Result();
|
||||
try
|
||||
{
|
||||
if (File.Exists(configFile.FullPath))
|
||||
return validationResult.Invalidate($"File '{configFile.FullPath}' not found.");
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return validationResult.Invalidate(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result> DeleteFileAsync(ConfigFile configFile)
|
||||
{
|
||||
var result = new Result();
|
||||
try
|
||||
{
|
||||
if (!File.Exists(configFile.FullPath))
|
||||
return result.Invalidate($"File '{configFile.FullPath}' not found.");
|
||||
|
||||
File.Delete(configFile.FullPath);
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return result.Invalidate(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task<Result> ValidateForSaveFileAsync(ConfigFile configFile)
|
||||
{
|
||||
var validationResult = new Result();
|
||||
try
|
||||
{
|
||||
if (File.Exists(configFile.FullPath))
|
||||
return validationResult.Invalidate($"File '{configFile.FullPath}' not found.");
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return validationResult.Invalidate(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result<ConfigFile>> SaveFileAsync(ConfigFile configFile)
|
||||
{
|
||||
var saveResult = new Result<ConfigFile>();
|
||||
try
|
||||
{
|
||||
await File.WriteAllTextAsync(Path.Combine(ConfigPaths.NginxPath, configFile.Folder, $"{configFile.Name}.conf"), configFile.Body, Encoding.UTF8);
|
||||
|
||||
return saveResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return saveResult.Invalidate(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task<Result> ValidateForSaveDraftFileAsync(ConfigFile configFile)
|
||||
{
|
||||
var validationResult = new Result();
|
||||
try
|
||||
{
|
||||
var draftPathName = $"{configFile.FullPath}.draft";
|
||||
|
||||
if (File.Exists(draftPathName))
|
||||
return validationResult.Invalidate($"File '{draftPathName}' not found.");
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return validationResult.Invalidate(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result<ConfigFile>> SaveDraftFileAsync(ConfigFile configFile)
|
||||
{
|
||||
var saveDraftResult = new Result<ConfigFile>();
|
||||
try
|
||||
{
|
||||
await File.WriteAllTextAsync(Path.Combine(ConfigPaths.NginxPath, configFile.Folder, $"{configFile.Name}.conf.draft"), configFile.DraftBody, Encoding.UTF8);
|
||||
|
||||
return saveDraftResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return saveDraftResult.Invalidate(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task<Result> ValidateForRenameFileAsync(List<ConfigFile> configFiles, ConfigFile selectedConfigFile, string newName)
|
||||
{
|
||||
var renameResult = new Result();
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
if(configFiles.Count(cf => cf.Name == newName) > 0)
|
||||
return renameResult.Invalidate($"File '{selectedConfigFile.FullPath}' already exists.");
|
||||
|
||||
|
||||
|
||||
|
||||
if (File.Exists(selectedConfigFile.FullPath))
|
||||
return renameResult.Invalidate($"Original file '{selectedConfigFile.FullPath}' not found.");
|
||||
|
||||
var newPathName = Path.Combine(ConfigPaths.NginxPath, selectedConfigFile.Folder, $"{newName}.conf");
|
||||
if (File.Exists(newPathName))
|
||||
return renameResult.Invalidate($"The file '{newPathName}' already exists.");
|
||||
|
||||
return renameResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return renameResult.Invalidate(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result> RenameFileAsync(ConfigFile configFile, string newName)
|
||||
{
|
||||
var renameResult = new Result();
|
||||
try
|
||||
{
|
||||
var originalPathName = Path.Combine(ConfigPaths.NginxPath, configFile.Folder, $"{configFile.Name}.conf");
|
||||
var newPathName = Path.Combine(ConfigPaths.NginxPath, configFile.Folder, newName);
|
||||
|
||||
File.Move(originalPathName, newPathName, overwrite: false);
|
||||
|
||||
return renameResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return renameResult.Invalidate(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<Result<string>> TestFileAsync(ConfigFile configFile)
|
||||
{
|
||||
await Task.Run(() => { });
|
||||
var result = new Result<string>();
|
||||
result.SetData("Uhu");
|
||||
return result;
|
||||
}
|
||||
|
||||
//public async Task<Result> DeleteFileAsync(ConfigFile configFile)
|
||||
//{
|
||||
// var result = new Result<bool>(true);
|
||||
// var result = new Result(true);
|
||||
// try
|
||||
// {
|
||||
// return result;
|
||||
|
@ -17,7 +17,7 @@
|
||||
@key="@ActiveNav.Keys.ElementAt(0)"
|
||||
ActiveClass="@ActiveNav.GetValueOrDefault("nginx")"
|
||||
@onclick="@(e => SelectMenuItem("nginx"))">
|
||||
<span class="mdi mdi-file-cog-outline"></span>
|
||||
<span class="mdi mdi-file-cog"></span>
|
||||
Configuration
|
||||
</NavLink>
|
||||
</li>
|
||||
@ -26,7 +26,7 @@
|
||||
@key="@ActiveNav.Keys.ElementAt(1)"
|
||||
ActiveClass="@ActiveNav.GetValueOrDefault("nginxlogs")"
|
||||
@onclick="@(e => SelectMenuItem("nginxlogs"))">
|
||||
<span class="mdi mdi-information-outline"></span>
|
||||
<span class="mdi mdi-information"></span>
|
||||
Logs
|
||||
</NavLink>
|
||||
</li>
|
||||
@ -41,7 +41,7 @@
|
||||
@key="@ActiveNav.Keys.ElementAt(2)"
|
||||
ActiveClass="@ActiveNav.GetValueOrDefault("systemd")"
|
||||
@onclick="@(e => SelectMenuItem("systemd"))">
|
||||
<span class="mdi mdi-file-cog-outline"></span>
|
||||
<span class="mdi mdi-file-cog"></span>
|
||||
Configuration
|
||||
</NavLink>
|
||||
</li>
|
||||
@ -50,7 +50,7 @@
|
||||
@key="@ActiveNav.Keys.ElementAt(3)"
|
||||
ActiveClass="@ActiveNav.GetValueOrDefault("systemdlogs")"
|
||||
@onclick="@(e => SelectMenuItem("systemdlogs"))">
|
||||
<span class="mdi mdi-information-outline"></span>
|
||||
<span class="mdi mdi-information"></span>
|
||||
Logs
|
||||
</NavLink>
|
||||
</li>
|
||||
@ -65,7 +65,7 @@
|
||||
@key="@ActiveNav.Keys.ElementAt(4)"
|
||||
ActiveClass="@ActiveNav.GetValueOrDefault("dmesg")"
|
||||
@onclick="@(e => SelectMenuItem("dmesg"))">
|
||||
<span class="mdi mdi-information-outline"></span>
|
||||
<span class="mdi mdi-information"></span>
|
||||
Logs
|
||||
</NavLink>
|
||||
</li>
|
||||
|
@ -21,7 +21,7 @@
|
||||
</select>
|
||||
</div>
|
||||
<span class="icon is-small is-left has-text-dark">
|
||||
<i class="mdi mdi-puzzle-outline"></i>
|
||||
<i class="mdi mdi-puzzle"></i>
|
||||
</span>
|
||||
</div>
|
||||
<p class="help">Any template to quick setup the configuration</p>
|
||||
@ -32,7 +32,7 @@
|
||||
<div class="control has-icons-left">
|
||||
<input class="input is-rounded is-small neoInput" type="text" placeholder="Name" @bind="TemplateName">
|
||||
<span class="icon is-small is-left has-text-dark">
|
||||
<i class="mdi mdi-file-code-outline"></i>
|
||||
<i class="mdi mdi-file-code"></i>
|
||||
</span>
|
||||
</div>
|
||||
<p class="help">Name it the same as the service which is going to run behind</p>
|
||||
|
65
Seenginx/Shared/RenameForm.razor
Normal file
65
Seenginx/Shared/RenameForm.razor
Normal file
@ -0,0 +1,65 @@
|
||||
<div class="modal-content neomorph">
|
||||
|
||||
<div class="modal-card-head">
|
||||
<h4 class="modal-card-title has-text-centered">
|
||||
Change the configuration name
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div class="modal-card-body">
|
||||
|
||||
<EditForm Model="Name">
|
||||
<div class="field">
|
||||
<label class="label">New configuration name</label>
|
||||
<div class="control has-icons-left">
|
||||
<input class="input is-rounded is-small neoInput" type="text" placeholder="Name" @bind="Name">
|
||||
<span class="icon is-small is-left has-text-dark">
|
||||
<i class="mdi mdi-file-code"></i>
|
||||
</span>
|
||||
</div>
|
||||
<DataAnnotationsValidator />
|
||||
<p class="help">Give a name coherently to the service which is going to run behind</p>
|
||||
</div>
|
||||
</EditForm>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-card-foot">
|
||||
|
||||
<div class="level fullwidth">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<button type="button" class="button is-rounded neoBtnSmall is-small has-text-dark" @onclick="Cancel">
|
||||
<span class="icon is-small">
|
||||
<i class="mdi mdi-close"></i>
|
||||
</span>
|
||||
<span>Close</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
<button type="submit" class="button is-primary is-rounded neoBtnSmall is-small has-text-dark" @onclick="SubmitForm">
|
||||
<span class="icon is-small">
|
||||
<i class="mdi mdi-pen"></i>
|
||||
</span>
|
||||
<span>Rename</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[CascadingParameter]
|
||||
BlazoredModalInstance BlazoredModal { get; set; }
|
||||
|
||||
[Required, Parameter, MaxLength(251)]
|
||||
public string Name { get; set; }
|
||||
|
||||
void SubmitForm() => BlazoredModal.Close(ModalResult.Ok(Name));
|
||||
void Cancel() => BlazoredModal.Cancel();
|
||||
}
|
@ -48,7 +48,6 @@ 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.
|
||||
|
1
Seenginx/wwwroot/css/bulma-switch.min.css
vendored
Normal file
1
Seenginx/wwwroot/css/bulma-switch.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -106,6 +106,10 @@ html {
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
transform: scale(1.1); }
|
||||
.neoBtnSmallPlain {
|
||||
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; }
|
||||
|
||||
.neoFile {
|
||||
box-shadow: 0px 0px 0px rgba(251, 238, 208, 0.5), 0px 0px 0px rgba(241, 185, 65, 0.5) !important;
|
||||
|
2
Seenginx/wwwroot/css/main.min.css
vendored
2
Seenginx/wwwroot/css/main.min.css
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user