updating nginx add and delete
This commit is contained in:
parent
720ed56432
commit
ca43e89583
@ -6,6 +6,5 @@ namespace Seenginx.Models
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string SelectedTemplate { get; set; } = "0";
|
||||
public List<Template> Templates { get; set; } = new List<Template>();
|
||||
}
|
||||
}
|
||||
|
@ -7,16 +7,12 @@ namespace Seenginx.Components
|
||||
{
|
||||
public class FileItemBase : ComponentBase
|
||||
{
|
||||
[Parameter]
|
||||
public ConfigFile File { get; set; }
|
||||
[Parameter]
|
||||
public EventCallback<ConfigFile> SelectedFileChanged { get; set; }
|
||||
[Parameter] public ConfigFile File { get; set; }
|
||||
[Parameter] public EventCallback<ConfigFile> SelectedFileChanged { get; set; }
|
||||
|
||||
public async Task SelectFile(MouseEventArgs e)
|
||||
{
|
||||
await SelectedFileChanged.InvokeAsync(File);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -5,17 +5,17 @@
|
||||
<div class="filterFiles">
|
||||
<div class="field has-addons">
|
||||
<div class="control has-icons-left is-expanded">
|
||||
<input formnovalidate @oninput="e => SearchInputChanged(e.Value.ToString())" class="input is-rounded is-small neoInput" type="text" placeholder="Search...">
|
||||
<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-search-web"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="control has-icons-left">
|
||||
<div class="select is-small is-rounded neoSelect">
|
||||
<select @onchange="e => OnFilterClick(e.Value.ToString())">
|
||||
<select @oninput="e => OnFilterClick(e.Value.ToString())" @bind="SelectedFilter">
|
||||
@foreach (var filter in Filters)
|
||||
{
|
||||
<option value="@filter">@filter</option>
|
||||
<option value="@filter" selected="@(SelectedFilter == filter ? "selected" : null)">@filter</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
@ -75,7 +75,7 @@
|
||||
|
||||
<div class="filesActions">
|
||||
<div class="buttons is-centered">
|
||||
<button class="button is-rounded neoBtnSmall is-small noBottomMargin" @onclick="OnAddDialog">
|
||||
<button class="button is-rounded neoBtnSmall is-small noBottomMargin" @onclick="async () => await OnAddDialog()">
|
||||
<span class="icon is-small has-text-success">
|
||||
<i class="mdi mdi-plus"></i>
|
||||
</span>
|
||||
|
@ -14,22 +14,23 @@ namespace Seenginx.Components
|
||||
public partial class FilesWithEditor<CFile> : ComponentBase
|
||||
where CFile : ConfigFile
|
||||
{
|
||||
[Inject]
|
||||
public IJSRuntime JsRuntime { get; set; }
|
||||
[Inject] public IJSRuntime JsRuntime { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public List<CFile> Files { get; set; } = new List<CFile>();
|
||||
[Parameter] public List<CFile> Files { get; set; } = new List<CFile>();
|
||||
|
||||
[Parameter]
|
||||
public List<string> Filters { get; set; } = new List<string>();
|
||||
[Parameter]
|
||||
public Dictionary<string, string> FilterFolder { get; set; } = new Dictionary<string, string>();
|
||||
[Parameter] public List<string> Filters { get; set; } = new List<string>();
|
||||
[Parameter] public Dictionary<string, string> FilterFolder { get; set; } = new Dictionary<string, string>();
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<CFile> UpdateFile { get; set; }
|
||||
[Parameter] public EventCallback<CFile> UpdateFile { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment<CFile> Editor { get; set; } = null;
|
||||
[Parameter] public RenderFragment<CFile> Editor { get; set; } = null;
|
||||
|
||||
[Parameter] public EventCallback TestConfiguration { get; set; }
|
||||
[Parameter] public Result<string> TestResult { get; set; }
|
||||
[Parameter] public EventCallback ShowAddFileModal { get; set; }
|
||||
[Parameter] public EventCallback DeleteFileCallback { get; set; }
|
||||
[Parameter] public EventCallback<CFile> SelectedFileChanged { get; set; }
|
||||
[Parameter] public CFile SelectedFile { get; set; } = default;
|
||||
|
||||
protected string SelectedFilter { get; set; }
|
||||
protected string SearchInput { get; set; }
|
||||
@ -46,11 +47,6 @@ namespace Seenginx.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<CFile> SelectedFileChanged { get; set; }
|
||||
[Parameter]
|
||||
public CFile SelectedFile { get; set; } = default;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
try
|
||||
@ -96,6 +92,7 @@ namespace Seenginx.Components
|
||||
if (SelectedFilter == "All")
|
||||
Files.ForEach(f => f.Unhide());
|
||||
else if (SelectedFilter == "Root")
|
||||
{
|
||||
Files.ForEach(f =>
|
||||
{
|
||||
if (f.Folder == FilterFolder[SelectedFilter])
|
||||
@ -103,6 +100,7 @@ namespace Seenginx.Components
|
||||
else
|
||||
f.Hide();
|
||||
});
|
||||
}
|
||||
else
|
||||
Files.ForEach(f =>
|
||||
{
|
||||
@ -121,7 +119,7 @@ namespace Seenginx.Components
|
||||
{
|
||||
if (SelectedFilter == "Root")
|
||||
{
|
||||
if (f.Folder == FilterFolder[SelectedFilter] && f.Folder.ToLower().Contains(SearchInput.ToLower()))
|
||||
if (f.Folder == FilterFolder[SelectedFilter] && f.Name.ToLower().Contains(SearchInput.ToLower()))
|
||||
f.Unhide();
|
||||
else
|
||||
f.Hide();
|
||||
@ -130,7 +128,7 @@ namespace Seenginx.Components
|
||||
{
|
||||
Files.ForEach(f =>
|
||||
{
|
||||
if (f.Folder.Contains(FilterFolder[SelectedFilter]) && f.Folder.ToLower().Contains(SearchInput.ToLower()))
|
||||
if (f.Folder.Contains(FilterFolder[SelectedFilter]) && f.Name.ToLower().Contains(SearchInput.ToLower()))
|
||||
f.Unhide();
|
||||
else
|
||||
f.Hide();
|
||||
@ -160,42 +158,44 @@ namespace Seenginx.Components
|
||||
var draftCode = await JsRuntime.InvokeAsync<string>("GetEditorCode");
|
||||
SelectedFile.DraftBody = draftCode;
|
||||
}
|
||||
|
||||
protected async Task OnUndoChanges(MouseEventArgs e)
|
||||
{
|
||||
SelectedFile.DraftBody = SelectedFile.Body;
|
||||
await JsRuntime.InvokeVoidAsync("UpdateEditor", SelectedFile.Body);
|
||||
}
|
||||
|
||||
protected async Task OnSave(MouseEventArgs e)
|
||||
{
|
||||
var draftCode = await JsRuntime.InvokeAsync<string>("GetEditorCode");
|
||||
SelectedFile.Body = draftCode;
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
public EventCallback TestConfiguration { get; set; }
|
||||
[Parameter]
|
||||
public Result<string> TestResult { get; set; }
|
||||
protected async Task OnTest(MouseEventArgs e)
|
||||
{
|
||||
await TestConfiguration.InvokeAsync(null);
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
public EventCallback AddFileModal { get; set; }
|
||||
protected async Task OnAddDialog()
|
||||
public async Task OnAddDialog()
|
||||
{
|
||||
await AddFileModal.InvokeAsync(null);
|
||||
await ShowAddFileModal.InvokeAsync(null);
|
||||
await JsRuntime.InvokeVoidAsync("ClearEditor");
|
||||
if (SelectedFile != null)
|
||||
{
|
||||
await JsRuntime.InvokeVoidAsync("UpdateEditor", SelectedFile.Body);
|
||||
SelectedFilter = "All";
|
||||
SearchInput = string.Empty;
|
||||
SearchFile();
|
||||
}
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
public GeneralNotificationModalBase DeleteFileModal { get; set; }
|
||||
protected void OnDeleteDialog()
|
||||
protected async Task OnDeleteDialog()
|
||||
{
|
||||
DeleteFileModal.Show(new NotificationSettings
|
||||
{
|
||||
PopupType = PopupType.YesNo,
|
||||
Text = $"Do you want to delete '{SelectedFile.Name}' configuration file?"
|
||||
});
|
||||
await DeleteFileCallback.InvokeAsync(null);
|
||||
await JsRuntime.InvokeVoidAsync("ClearEditor");
|
||||
SelectedFilter = "All";
|
||||
SearchInput = string.Empty;
|
||||
SearchFile();
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,105 +0,0 @@
|
||||
@inherits GeneralNotificationModalBase
|
||||
|
||||
<Modal @ref="ModalReference">
|
||||
<ModalBackdrop />
|
||||
<ModalContent>
|
||||
<ModalBody>
|
||||
<p>@NotificationSettings.Text</p>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<div class="level fullwidth">
|
||||
@switch (NotificationSettings.PopupType)
|
||||
{
|
||||
case PopupType.Ok:
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
<Blazorise.Bulma.Button Clicked="Ok" Class="is-rounded neoBtnSmall is-small has-text-dark">
|
||||
<span class="icon is-small has-text-success">
|
||||
<i class="mdi mdi-check"></i>
|
||||
</span>
|
||||
<span>Ok</span>
|
||||
</Blazorise.Bulma.Button>
|
||||
</div>
|
||||
</div>
|
||||
break;
|
||||
case PopupType.OkCancel:
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<Blazorise.Bulma.Button Clicked="Cancel" Class="is-rounded neoBtnSmall is-small has-text-dark">
|
||||
<span class="icon is-small">
|
||||
<i class="mdi mdi-close"></i>
|
||||
</span>
|
||||
<span>Cancel</span>
|
||||
</Blazorise.Bulma.Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
<Blazorise.Bulma.Button Clicked="Ok" Class="is-rounded neoBtnSmall is-small has-text-dark" Type="ButtonType.Submit">
|
||||
<span class="icon is-small has-text-success">
|
||||
<i class="mdi mdi-check"></i>
|
||||
</span>
|
||||
<span>Ok</span>
|
||||
</Blazorise.Bulma.Button>
|
||||
</div>
|
||||
</div>
|
||||
break;
|
||||
case PopupType.YesNo:
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<Blazorise.Bulma.Button Clicked="No" Class="is-rounded neoBtnSmall is-small has-text-dark">
|
||||
<span class="icon is-small has-text-danger">
|
||||
<i class="mdi mdi-close"></i>
|
||||
</span>
|
||||
<span>No</span>
|
||||
</Blazorise.Bulma.Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
<Blazorise.Bulma.Button Clicked="Yes" Class="is-rounded neoBtnSmall is-small has-text-dark" Type="ButtonType.Submit">
|
||||
<span class="icon is-small has-text-success">
|
||||
<i class="mdi mdi-check"></i>
|
||||
</span>
|
||||
<span>Yes</span>
|
||||
</Blazorise.Bulma.Button>
|
||||
</div>
|
||||
</div>
|
||||
break;
|
||||
case PopupType.YesNoCancel:
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<Blazorise.Bulma.Button Clicked="Cancel" Class="is-rounded neoBtnSmall is-small has-text-dark">
|
||||
<span class="icon is-small">
|
||||
<i class="mdi mdi-close"></i>
|
||||
</span>
|
||||
<span>Cancel</span>
|
||||
</Blazorise.Bulma.Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
<Blazorise.Bulma.Button Clicked="No" Class="is-rounded neoBtnSmall is-small has-text-dark">
|
||||
<span class="icon is-small has-text-danger">
|
||||
<i class="mdi mdi-close"></i>
|
||||
</span>
|
||||
<span>No</span>
|
||||
</Blazorise.Bulma.Button>
|
||||
</div>
|
||||
<div class="level-item">
|
||||
<Blazorise.Bulma.Button Clicked="Yes" Class="is-rounded neoBtnSmall is-small has-text-dark" Type="ButtonType.Submit">
|
||||
<span class="icon is-small has-text-success">
|
||||
<i class="mdi mdi-check"></i>
|
||||
</span>
|
||||
<span>Yes</span>
|
||||
</Blazorise.Bulma.Button>
|
||||
</div>
|
||||
</div>
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
@ -1,29 +0,0 @@
|
||||
using Blazorise;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Seenginx.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Seenginx.Components
|
||||
{
|
||||
public class GeneralNotificationModalBase : ComponentBase
|
||||
{
|
||||
public Modal ModalReference { get; set; } = new Modal();
|
||||
public NotificationSettings NotificationSettings { get; set; }
|
||||
[Parameter]
|
||||
public EventCallback<PopupAnswer> PopupCallback { get; set; }
|
||||
|
||||
public void Show(NotificationSettings notificationSettings)
|
||||
{
|
||||
NotificationSettings = notificationSettings;
|
||||
ModalReference.Show();
|
||||
}
|
||||
|
||||
public async Task Ok() => await PopupCallback.InvokeAsync(PopupAnswer.Ok);
|
||||
public async Task Cancel() => await PopupCallback.InvokeAsync(PopupAnswer.Cancel);
|
||||
public async Task Yes() => await PopupCallback.InvokeAsync(PopupAnswer.Yes);
|
||||
public async Task No() => await PopupCallback.InvokeAsync(PopupAnswer.No);
|
||||
}
|
||||
}
|
@ -3,72 +3,6 @@
|
||||
<FilesWithEditor CFile="ConfigFile" Filters="Filters" Files="ConfigFiles" FilterFolder="FilterFolder"
|
||||
SelectedFile="SelectedFile" SelectedFileChanged="SelectedFileChanged"
|
||||
TestConfiguration="TestConfiguration" TestResult="TestResult"
|
||||
DeleteFileModal="DeleteNotificationModal" AddFileModal="ShowAddFileModal">
|
||||
DeleteFileCallback="DeleteFile" ShowAddFileModal="ShowAddFileModal" >
|
||||
</FilesWithEditor>
|
||||
<Modal @ref="AddFileModal">
|
||||
<ModalBackdrop />
|
||||
<ModalContent Class="neomorph">
|
||||
<ModalHeader>
|
||||
<ModalTitle Class="has-text-centered">Add a new configuration for a service</ModalTitle>
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<div class="field">
|
||||
<label class="label">Template</label>
|
||||
<div class="control has-icons-left">
|
||||
<div class="select is-small is-rounded neoSelect fullwidth">
|
||||
<select class="fullwidth" @bind="NewFileForm.SelectedTemplate">
|
||||
<option value="0">No template</option>
|
||||
@foreach (var template in NewFileForm.Templates)
|
||||
{
|
||||
<option value="@template.Name">@($"{template.Name.First().ToString().ToUpper()}{template.Name.Substring(1)}")</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<span class="icon is-small is-left has-text-dark">
|
||||
<i class="mdi mdi-puzzle-outline"></i>
|
||||
</span>
|
||||
</div>
|
||||
<p class="help">Any template to quick setup the configuration</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Configuration file name</label>
|
||||
<div class="control has-icons-left has-icons-right">
|
||||
<input class="input is-rounded is-small neoInput" type="text" @bind="NewFileForm.Name" placeholder="Name" />
|
||||
<span class="icon is-small is-left has-text-dark">
|
||||
<i class="mdi mdi-file-code-outline"></i>
|
||||
</span>
|
||||
</div>
|
||||
<p class="help">Name it the same as the service which is going to run behind</p>
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<div class="level fullwidth">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<Blazorise.Bulma.Button Clicked="e => CloseModal(AddFileModal)"
|
||||
Class="is-rounded neoBtnSmall is-small has-text-dark">
|
||||
<span class="icon is-small">
|
||||
<i class="mdi mdi-close"></i>
|
||||
</span>
|
||||
<span>Close</span>
|
||||
</Blazorise.Bulma.Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
<Blazorise.Bulma.Button Color="Color.Primary" Clicked="AddFileAsync"
|
||||
Class="is-rounded neoBtnSmall is-small has-text-dark" Type="ButtonType.Submit">
|
||||
<span class="icon is-small has-text-success">
|
||||
<i class="mdi mdi-plus"></i>
|
||||
</span>
|
||||
<span>Add</span>
|
||||
</Blazorise.Bulma.Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
<GeneralNotificationModal PopupCallback="DeleteFile"></GeneralNotificationModal>
|
||||
@*<GeneralNotificationModal ></GeneralNotificationModal>*@
|
||||
|
@ -1,23 +1,23 @@
|
||||
using Blazorise;
|
||||
using Blazored.Modal;
|
||||
using Blazored.Modal.Services;
|
||||
using Blazorise;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Seenginx.Components;
|
||||
using Seenginx.Models;
|
||||
using Seenginx.Services;
|
||||
using Seenginx.Shared;
|
||||
using Seenginx.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Seenginx.Pages
|
||||
{
|
||||
public class NginxBase : ComponentBase
|
||||
{
|
||||
[Inject]
|
||||
public INginxService NginxService { get; set; }
|
||||
[Inject]
|
||||
public IFileManager FileService { get; set; }
|
||||
[Inject] public INginxService NginxService { get; set; }
|
||||
[Inject] public IFileManager FileService { get; set; }
|
||||
[Inject] public IModalService Modal { get; set; }
|
||||
|
||||
public string InputSearch { get; set; }
|
||||
|
||||
@ -26,6 +26,7 @@ namespace Seenginx.Pages
|
||||
public List<string> Filters { get; set; } = new List<string>();
|
||||
public List<int> FilteredOutFiles { get; set; } = new List<int>();
|
||||
public NotificationSettings GeneralNotificationSettings { get; set; } = null;
|
||||
public List<Template> Templates { get; set; } = new List<Template>();
|
||||
|
||||
public Dictionary<string, string> FilterFolder { get; set; } = new Dictionary<string, string>();
|
||||
|
||||
@ -40,7 +41,7 @@ namespace Seenginx.Pages
|
||||
FilterFolder.Add("Conf.d", "/conf.d");
|
||||
FilterFolder.Add("Available", "/sites-available");
|
||||
FilterFolder.Add("Enabled", "/sites-enabled");
|
||||
NewFileForm.Templates.AddRange(await NginxService.GetTemplates());
|
||||
Templates.AddRange(await NginxService.GetTemplates());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -54,83 +55,71 @@ namespace Seenginx.Pages
|
||||
SelectedFile = configFile;
|
||||
}
|
||||
|
||||
|
||||
protected Modal AddFileModal { get; set; }
|
||||
public void ShowAddFileModal()
|
||||
public async Task ShowAddFileModal()
|
||||
{
|
||||
AddFileModal.Show();
|
||||
}
|
||||
var parameters = new ModalParameters();
|
||||
parameters.Add(nameof(Templates), Templates);
|
||||
|
||||
public NewFileForm NewFileForm { get; set; } = new NewFileForm();
|
||||
public async Task AddFileAsync()
|
||||
{
|
||||
var addFileResult = await NginxService.AddFileAsync(NewFileForm);
|
||||
var resultAwait = Modal.Show<NginxConfigForm>(string.Empty, parameters);
|
||||
var result = await resultAwait.Result;
|
||||
if (result.Cancelled) return;
|
||||
|
||||
if (!addFileResult.AllOk)
|
||||
throw new Exception(":/");
|
||||
|
||||
var validationResult = await NginxService.ValidateNewConfigurationAsync((NewFileForm)result.Data);
|
||||
|
||||
if (!validationResult.AllOk)
|
||||
{
|
||||
var validationPopupParameters = new ModalParameters().Setup(PopupType.Ok, validationResult.ErrorMessage);
|
||||
var validationPopup = Modal.Show<GenericPopup>(string.Empty, validationPopupParameters);
|
||||
await validationPopup.Result;
|
||||
validationPopup.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
var addFileResult = await NginxService.AddFileAsync((NewFileForm)result.Data);
|
||||
if (SelectedFile != null)
|
||||
SelectedFile.Deselect();
|
||||
ConfigFiles.Add(addFileResult.Data);
|
||||
ConfigFiles = ConfigFiles.OrderBy(cf => cf.Name).ToList();
|
||||
AddFileModal.Hide();
|
||||
//if (AddFileResult.AllOk)
|
||||
// ConfigFiles.Add(AddFileResult.Data);
|
||||
//else
|
||||
//{
|
||||
// GeneralNotificationSettings = new GeneralNotificationSettings
|
||||
// {
|
||||
// ButtonColor = Color.Danger,
|
||||
// TitleClass = "mdi-error",
|
||||
// Title = "Failure",
|
||||
// Text = TestResult.ErrorMessage
|
||||
// };
|
||||
// GeneralNotificationModal.Show();
|
||||
//}
|
||||
SelectedFile = ConfigFiles.Find(cf => cf.Name == addFileResult.Data.Name);
|
||||
SelectedFile.Select();
|
||||
}
|
||||
|
||||
public Result<ConfigFile> SaveUpdateDraftResult { get; set; }
|
||||
public async Task SaveUpdateDraftFileAsync()
|
||||
{
|
||||
SaveUpdateDraftResult = await FileService.SaveUpdateDraftFileAsync(SelectedFile);
|
||||
var saveUpdateDraftResult = await FileService.SaveUpdateDraftFileAsync(SelectedFile);
|
||||
}
|
||||
|
||||
public Result<ConfigFile> SaveUpdateResult { get; set; }
|
||||
public async Task SaveUpdateFileAsync()
|
||||
{
|
||||
SaveUpdateResult = await FileService.SaveUpdateFileAsync(SelectedFile);
|
||||
var saveUpdateResult = await FileService.SaveUpdateFileAsync(SelectedFile);
|
||||
}
|
||||
|
||||
public Result<string> TestResult { get; set; }
|
||||
public Modal TestNotificationModal { get; set; } = new Modal();
|
||||
public async Task TestConfiguration()
|
||||
{
|
||||
TestResult = await NginxService.TestNginxConfigurations(SelectedFile);
|
||||
if (TestResult.AllOk)
|
||||
GeneralNotificationSettings = new NotificationSettings
|
||||
{
|
||||
Text = $"Test of config file '{SelectedFile.Name}' completed with success.",
|
||||
PopupType = PopupType.Ok
|
||||
};
|
||||
else
|
||||
GeneralNotificationSettings = new NotificationSettings
|
||||
{
|
||||
Text = TestResult.ErrorMessage,
|
||||
PopupType = PopupType.Ok
|
||||
};
|
||||
TestNotificationModal.Show();
|
||||
|
||||
}
|
||||
|
||||
public GeneralNotificationModalBase DeleteNotificationModal { get; set; } = new GeneralNotificationModalBase();
|
||||
public void DeleteFile(PopupAnswer popupAnswer)
|
||||
public async Task DeleteFile()
|
||||
{
|
||||
if (popupAnswer == PopupAnswer.Yes)
|
||||
var parameters = new ModalParameters().Setup(PopupType.YesNo, $"Are you sure you want to delete '{SelectedFile.Name}'?");
|
||||
|
||||
var resultAwait = Modal.Show<GenericPopup>(string.Empty, parameters);
|
||||
var result = await resultAwait.Result;
|
||||
if ((PopupAnswer)result.Data == PopupAnswer.No) return;
|
||||
|
||||
var deleteFileResult = await NginxService.DeleteConfigurationFileAsync(SelectedFile);
|
||||
|
||||
if (!deleteFileResult.AllOk)
|
||||
{
|
||||
var deleteResult = FileService.DeleteFile(SelectedFile);
|
||||
var errorParameters = new ModalParameters().Setup(PopupType.Ok, $"Something went wrong, here's the error message: '{deleteFileResult.ErrorMessage}'?");
|
||||
var errorReportModalAwait = Modal.Show<GenericPopup>(string.Empty, errorParameters);
|
||||
await errorReportModalAwait.Result;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void CloseModal(Modal modal)
|
||||
{
|
||||
modal.Hide();
|
||||
ConfigFiles.Remove(SelectedFile);
|
||||
SelectedFile = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,9 +53,6 @@ html {
|
||||
}
|
||||
|
||||
.modal- {
|
||||
&background {
|
||||
background: rgba($background,.9);
|
||||
}
|
||||
|
||||
&content {
|
||||
border-radius: $border-radius-b;
|
||||
@ -81,3 +78,22 @@ html {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.blazored {
|
||||
&-modal {
|
||||
background-color: transparent;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
|
||||
&-container {
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&-overlay {
|
||||
background: $background
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -149,6 +149,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
.field.has-addons {
|
||||
& > .control.is-expanded > .neoInput {
|
||||
box-shadow: inset 2px 2px 4px rgba($dark-shadow, .5),inset 2px -2px 4px rgba($light-shadow, .5) !important;
|
||||
}
|
||||
|
||||
& > .control > .neoSelect > select {
|
||||
box-shadow: inset -2px 2px 4px rgba($dark-shadow, .5),inset -2px -2px 4px rgba($light-shadow, .5) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.gradientBackground {
|
||||
background: linear-gradient(to right bottom,$background-light,$background-dark)
|
||||
}
|
||||
|
@ -10,5 +10,7 @@ namespace Seenginx.Services
|
||||
Task<Result<string>> TestNginxConfigurations(ConfigFile configFile);
|
||||
Task<Result<ConfigFile>> AddFileAsync(NewFileForm newFileForm);
|
||||
Task<IEnumerable<Template>> GetTemplates();
|
||||
Task<Result<bool>> DeleteConfigurationFileAsync(ConfigFile configFile);
|
||||
Task<Result<bool>> ValidateNewConfigurationAsync(NewFileForm newFileForm);
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.AccessControl;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -19,22 +20,47 @@ 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)
|
||||
{
|
||||
await Task.Run(() => { });
|
||||
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;
|
||||
|
||||
await File.WriteAllTextAsync(newFile.FullPath, newFile.Body, Encoding.UTF8);
|
||||
|
||||
var addResult = new Result<ConfigFile>();
|
||||
addResult.SetData(newFile);
|
||||
try
|
||||
{
|
||||
|
||||
return addResult;
|
||||
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;
|
||||
|
||||
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<IEnumerable<ConfigFile>> GetFilesAsync()
|
||||
@ -53,7 +79,7 @@ namespace Seenginx.Services
|
||||
configFile.Folder = "/";
|
||||
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.Name);
|
||||
configFile.Body = File.ReadAllText(fp);
|
||||
return configFile;
|
||||
});
|
||||
@ -65,7 +91,7 @@ namespace Seenginx.Services
|
||||
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.Replace("/", string.Empty), configFile.Name);
|
||||
configFile.Body = File.ReadAllText(fp);
|
||||
return configFile;
|
||||
});
|
||||
@ -77,7 +103,7 @@ namespace Seenginx.Services
|
||||
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.Replace("/", string.Empty), configFile.Name);
|
||||
configFile.Body = File.ReadAllText(fp);
|
||||
return configFile;
|
||||
});
|
||||
@ -89,7 +115,7 @@ namespace Seenginx.Services
|
||||
configFile.Folder = "/sites-enabled";
|
||||
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.Replace("/", string.Empty), configFile.Name);
|
||||
configFile.Body = File.ReadAllText(fp);
|
||||
return configFile;
|
||||
});
|
||||
@ -108,9 +134,7 @@ namespace Seenginx.Services
|
||||
try
|
||||
{
|
||||
var nginxTemplateDirectory = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "templates", "nginx");
|
||||
//var systemdTemplateDirectory = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "systemd");
|
||||
var nginxTemplateFiles = Directory.GetFiles(nginxTemplateDirectory, "*.template");
|
||||
//var systemdTemplateFiles = Directory.GetFiles(nginxTemplateDirectory, "*.template");
|
||||
|
||||
foreach (var templateFilePath in nginxTemplateFiles)
|
||||
{
|
||||
@ -136,5 +160,38 @@ namespace Seenginx.Services
|
||||
result.SetData("Uhu");
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Result<bool>> DeleteConfigurationFileAsync(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<bool>> DeleteFileAsync(ConfigFile configFile)
|
||||
//{
|
||||
// var result = new Result<bool>(true);
|
||||
// try
|
||||
// {
|
||||
// return result;
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// return result.Invalidate(ex.Message, ex);
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
130
Seenginx/Shared/GenericPopup.razor
Normal file
130
Seenginx/Shared/GenericPopup.razor
Normal file
@ -0,0 +1,130 @@
|
||||
<div class="modal-content neomorph">
|
||||
|
||||
@*<div class="modal-card-head">
|
||||
<h4 class="modal-card-title has-text-centered">
|
||||
Add a new configuration for a service
|
||||
</h4>
|
||||
</div>*@
|
||||
|
||||
<div class="modal-card-body">
|
||||
|
||||
<h1>@Message</h1>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-card-foot">
|
||||
|
||||
<div class="level fullwidth">
|
||||
@switch (PopupType)
|
||||
{
|
||||
case PopupType.Ok:
|
||||
<div class="level-left">
|
||||
<div class="level-item"></div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
<Blazorise.Bulma.Button Clicked="Ok" Class="is-rounded neoBtnSmall is-small has-text-dark">
|
||||
<span class="icon is-small has-text-success">
|
||||
<i class="mdi mdi-check"></i>
|
||||
</span>
|
||||
<span>Ok</span>
|
||||
</Blazorise.Bulma.Button>
|
||||
</div>
|
||||
</div>
|
||||
break;
|
||||
case PopupType.OkCancel:
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<Blazorise.Bulma.Button Clicked="Cancel" Class="is-rounded neoBtnSmall is-small has-text-dark">
|
||||
<span class="icon is-small">
|
||||
<i class="mdi mdi-close"></i>
|
||||
</span>
|
||||
<span>Cancel</span>
|
||||
</Blazorise.Bulma.Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
<Blazorise.Bulma.Button Clicked="Ok" Class="is-rounded neoBtnSmall is-small has-text-dark" Type="ButtonType.Submit">
|
||||
<span class="icon is-small has-text-success">
|
||||
<i class="mdi mdi-check"></i>
|
||||
</span>
|
||||
<span>Ok</span>
|
||||
</Blazorise.Bulma.Button>
|
||||
</div>
|
||||
</div>
|
||||
break;
|
||||
case PopupType.YesNo:
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<Blazorise.Bulma.Button Clicked="No" Class="is-rounded neoBtnSmall is-small has-text-dark">
|
||||
<span class="icon is-small has-text-danger">
|
||||
<i class="mdi mdi-close"></i>
|
||||
</span>
|
||||
<span>No</span>
|
||||
</Blazorise.Bulma.Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
<Blazorise.Bulma.Button Clicked="Yes" Class="is-rounded neoBtnSmall is-small has-text-dark" Type="ButtonType.Submit">
|
||||
<span class="icon is-small has-text-success">
|
||||
<i class="mdi mdi-check"></i>
|
||||
</span>
|
||||
<span>Yes</span>
|
||||
</Blazorise.Bulma.Button>
|
||||
</div>
|
||||
</div>
|
||||
break;
|
||||
case PopupType.YesNoCancel:
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<Blazorise.Bulma.Button Clicked="Cancel" Class="is-rounded neoBtnSmall is-small has-text-dark">
|
||||
<span class="icon is-small">
|
||||
<i class="mdi mdi-close"></i>
|
||||
</span>
|
||||
<span>Cancel</span>
|
||||
</Blazorise.Bulma.Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
<Blazorise.Bulma.Button Clicked="No" Class="is-rounded neoBtnSmall is-small has-text-dark">
|
||||
<span class="icon is-small has-text-danger">
|
||||
<i class="mdi mdi-close"></i>
|
||||
</span>
|
||||
<span>No</span>
|
||||
</Blazorise.Bulma.Button>
|
||||
</div>
|
||||
<div class="level-item">
|
||||
<Blazorise.Bulma.Button Clicked="Yes" Class="is-rounded neoBtnSmall is-small has-text-dark" Type="ButtonType.Submit">
|
||||
<span class="icon is-small has-text-success">
|
||||
<i class="mdi mdi-check"></i>
|
||||
</span>
|
||||
<span>Yes</span>
|
||||
</Blazorise.Bulma.Button>
|
||||
</div>
|
||||
</div>
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[CascadingParameter]
|
||||
BlazoredModalInstance BlazoredModal { get; set; }
|
||||
|
||||
[Parameter, Required]
|
||||
public string Message { get; set; }
|
||||
[Parameter, Required]
|
||||
public PopupType PopupType { get; set; }
|
||||
|
||||
void Yes() => BlazoredModal.Close(ModalResult.Ok(PopupAnswer.Yes));
|
||||
void No() => BlazoredModal.Close(ModalResult.Ok(PopupAnswer.No));
|
||||
void Ok() => BlazoredModal.Close(ModalResult.Ok(PopupAnswer.Ok));
|
||||
void Cancel() => BlazoredModal.Close(ModalResult.Ok(PopupAnswer.Cancel));
|
||||
}
|
@ -10,5 +10,6 @@
|
||||
@Body
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
<BlazoredModal DisableBackgroundCancel="true" HideCloseButton="true" HideHeader="true" Position="ModalPosition.Center" />
|
84
Seenginx/Shared/NginxConfigForm.razor
Normal file
84
Seenginx/Shared/NginxConfigForm.razor
Normal file
@ -0,0 +1,84 @@
|
||||
<div class="modal-content neomorph">
|
||||
|
||||
<div class="modal-card-head">
|
||||
<h4 class="modal-card-title has-text-centered">
|
||||
Add a new configuration for a service
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div class="modal-card-body">
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Template</label>
|
||||
<div class="control has-icons-left">
|
||||
<div class="select is-small is-rounded neoSelect fullwidth">
|
||||
<select class="fullwidth" @bind="SelectedTemplate">
|
||||
<option value="0">No template</option>
|
||||
@foreach (var template in Templates)
|
||||
{
|
||||
<option value="@template.Name">@($"{template.Name.First().ToString().ToUpper()}{template.Name.Substring(1)}")</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<span class="icon is-small is-left has-text-dark">
|
||||
<i class="mdi mdi-puzzle-outline"></i>
|
||||
</span>
|
||||
</div>
|
||||
<p class="help">Any template to quick setup the configuration</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Configuration file name</label>
|
||||
<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>
|
||||
</span>
|
||||
</div>
|
||||
<p class="help">Name it the same as the service which is going to run behind</p>
|
||||
</div>
|
||||
|
||||
</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 has-text-success">
|
||||
<i class="mdi mdi-plus"></i>
|
||||
</span>
|
||||
<span>Add</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[CascadingParameter]
|
||||
BlazoredModalInstance BlazoredModal { get; set; }
|
||||
|
||||
string SelectedTemplate { get; set; } = "0";
|
||||
[Required]
|
||||
string TemplateName { get; set; }
|
||||
[Parameter]
|
||||
public List<Template> Templates { get; set; } = new List<Template>();
|
||||
|
||||
void SubmitForm() => BlazoredModal.Close(ModalResult.Ok(new NewFileForm { Name = TemplateName, SelectedTemplate = SelectedTemplate }));
|
||||
void Cancel() => BlazoredModal.Cancel();
|
||||
}
|
20
Seenginx/Utility/ModalUtility.cs
Normal file
20
Seenginx/Utility/ModalUtility.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using Blazored.Modal;
|
||||
using Mono.Posix;
|
||||
using Seenginx.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Seenginx.Utility
|
||||
{
|
||||
public static class ModalUtility
|
||||
{
|
||||
public static ModalParameters Setup(this ModalParameters parameters, PopupType popupType, string message)
|
||||
{
|
||||
parameters.Add(nameof(PopupType), popupType);
|
||||
parameters.Add("Message", message);
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
@using System.Net.Http
|
||||
@using System.ComponentModel.DataAnnotations
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
|
@ -133,6 +133,12 @@ html {
|
||||
.neoSelect > select:focus {
|
||||
border: none !important; }
|
||||
|
||||
.field.has-addons > .control.is-expanded > .neoInput {
|
||||
box-shadow: inset 2px 2px 4px rgba(241, 185, 65, 0.5), inset 2px -2px 4px rgba(251, 238, 208, 0.5) !important; }
|
||||
|
||||
.field.has-addons > .control > .neoSelect > select {
|
||||
box-shadow: inset -2px 2px 4px rgba(241, 185, 65, 0.5), inset -2px -2px 4px rgba(251, 238, 208, 0.5) !important; }
|
||||
|
||||
.gradientBackground {
|
||||
background: linear-gradient(to right bottom, #f7d794, #f5cd79); }
|
||||
|
||||
@ -197,9 +203,6 @@ html {
|
||||
.ace-solarized-light .ace_marker-layer .ace_active-line {
|
||||
border-radius: 0 50px 50px 0; }
|
||||
|
||||
.modal-background {
|
||||
background: rgba(246, 210, 135, 0.9); }
|
||||
|
||||
.modal-content {
|
||||
border-radius: 28px; }
|
||||
|
||||
@ -217,6 +220,18 @@ html {
|
||||
border: none;
|
||||
border-radius: 0; }
|
||||
|
||||
.blazored-modal {
|
||||
background-color: transparent;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
padding: 0;
|
||||
box-shadow: none; }
|
||||
.blazored-modal-container {
|
||||
left: 0;
|
||||
top: 0; }
|
||||
.blazored-modal-overlay {
|
||||
background: #f6d287; }
|
||||
|
||||
:root {
|
||||
--stripe-size: 200px;
|
||||
--color1: #f6d287;
|
||||
|
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