|
@@ -6,7 +6,7 @@
|
|
|
@using Moonlight.App.Exceptions
|
|
|
@using Moonlight.App.Services.Interop
|
|
|
@using Logging.Net
|
|
|
-@using Blazored.Typeahead
|
|
|
+@using Moonlight.App.Models.Forms
|
|
|
|
|
|
@inject NodeRepository NodeRepository
|
|
|
@inject ImageRepository ImageRepository
|
|
@@ -19,226 +19,182 @@
|
|
|
|
|
|
<OnlyAdmin>
|
|
|
<LazyLoader Load="Load">
|
|
|
- <div class="row mb-5">
|
|
|
- <div class="card card-body p-10">
|
|
|
- <label class="form-label">
|
|
|
- <TL>Server name</TL>
|
|
|
- </label>
|
|
|
- <div class="input-group mb-5">
|
|
|
- <span class="input-group-text">
|
|
|
- <i class="bx bx-purchase-tag-alt"></i>
|
|
|
- </span>
|
|
|
- <input @bind="Name" type="text" class="form-control" placeholder="@(SmartTranslateService.Translate("Server name"))" aria-label="Servername">
|
|
|
- </div>
|
|
|
- <label class="form-label">
|
|
|
- <TL>Server name</TL>
|
|
|
- </label>
|
|
|
- <div class="input-group mb-5">
|
|
|
- <div class="form-select">
|
|
|
- <BlazoredTypeahead SearchMethod="SearchUsers"
|
|
|
- @bind-Value="User">
|
|
|
- <SelectedTemplate>
|
|
|
- @(context.Email)
|
|
|
- </SelectedTemplate>
|
|
|
- <ResultTemplate>
|
|
|
- @(context.Email)
|
|
|
- </ResultTemplate>
|
|
|
- </BlazoredTypeahead>
|
|
|
+ <SmartForm Model="Model" OnValidSubmit="Create">
|
|
|
+ <div class="row mb-5">
|
|
|
+ <div class="card card-body p-10">
|
|
|
+ <label class="form-label">
|
|
|
+ <TL>Server name</TL>
|
|
|
+ </label>
|
|
|
+ <div class="input-group mb-5">
|
|
|
+ <span class="input-group-text">
|
|
|
+ <i class="bx bx-purchase-tag-alt"></i>
|
|
|
+ </span>
|
|
|
+ <InputText @bind-Value="Model.Name" type="text" class="form-control" placeholder="@(SmartTranslateService.Translate("Server name"))"></InputText>
|
|
|
+ </div>
|
|
|
+ <label class="form-label">
|
|
|
+ <TL>Owner</TL>
|
|
|
+ </label>
|
|
|
+ <div class="input-group mb-5">
|
|
|
+ <SmartDropdown
|
|
|
+ @bind-Value="Model.Owner"
|
|
|
+ Items="Users"
|
|
|
+ DisplayFunc="@(x => x.Email)"
|
|
|
+ SearchProp="@(x => x.Email)">
|
|
|
+ </SmartDropdown>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- <div class="row mb-5">
|
|
|
- <div class="card card-body p-10">
|
|
|
- <label class="form-label">
|
|
|
- <TL>Cpu cores</TL>
|
|
|
- </label>
|
|
|
- <div class="input-group mb-5">
|
|
|
- <span class="input-group-text">
|
|
|
- <i class="bx bx-chip"></i>
|
|
|
- </span>
|
|
|
- <input @bind="Cpu" type="number" class="form-control">
|
|
|
- <span class="input-group-text">
|
|
|
- <TL>CPU Cores (100% = 1 Core)</TL>
|
|
|
- </span>
|
|
|
- </div>
|
|
|
- <label class="form-label">
|
|
|
- <TL>Memory</TL>
|
|
|
- </label>
|
|
|
- <div class="input-group mb-5">
|
|
|
- <span class="input-group-text">
|
|
|
- <i class="bx bx-microchip"></i>
|
|
|
- </span>
|
|
|
- <input @bind="Memory" type="number" class="form-control">
|
|
|
- <span class="input-group-text">
|
|
|
- MB
|
|
|
- </span>
|
|
|
- </div>
|
|
|
- <label class="form-label">
|
|
|
- <TL>Disk</TL>
|
|
|
- </label>
|
|
|
- <div class="input-group mb-5">
|
|
|
- <span class="input-group-text">
|
|
|
- <i class="bx bx-hdd"></i>
|
|
|
- </span>
|
|
|
- <input @bind="Disk" type="number" class="form-control">
|
|
|
- <span class="input-group-text">
|
|
|
- MB
|
|
|
- </span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="row mb-5">
|
|
|
- <div class="card card-body p-10">
|
|
|
- <label class="form-label">
|
|
|
- <TL>Image</TL>
|
|
|
- </label>
|
|
|
- <select @bind="ImageIndex" class="form-select mb-5">
|
|
|
- @foreach (var image in Images)
|
|
|
- {
|
|
|
- <option value="@(Images.IndexOf(image))">@(image.Name)</option>
|
|
|
- }
|
|
|
- </select>
|
|
|
- @if (Image != null)
|
|
|
- {
|
|
|
+ <div class="row mb-5">
|
|
|
+ <div class="card card-body p-10">
|
|
|
<label class="form-label">
|
|
|
- <TL>Override startup</TL>
|
|
|
+ <TL>Cpu cores</TL>
|
|
|
</label>
|
|
|
<div class="input-group mb-5">
|
|
|
<span class="input-group-text">
|
|
|
- <i class="bx bx-terminal"></i>
|
|
|
+ <i class="bx bx-chip"></i>
|
|
|
+ </span>
|
|
|
+ <InputNumber @bind-Value="Model.Cpu" class="form-control"></InputNumber>
|
|
|
+ <span class="input-group-text">
|
|
|
+ <TL>CPU Cores (100% = 1 Core)</TL>
|
|
|
</span>
|
|
|
- <input @bind="OverrideStartup" type="text" class="form-control" placeholder="@(Image.Startup)">
|
|
|
</div>
|
|
|
<label class="form-label">
|
|
|
- <TL>Docker image</TL>
|
|
|
+ <TL>Memory</TL>
|
|
|
</label>
|
|
|
- <select @bind="DockerImageIndex" class="form-select">
|
|
|
- @foreach (var image in Image.DockerImages)
|
|
|
- {
|
|
|
- <option value="@(Image.DockerImages.IndexOf(image))">@(image.Name)</option>
|
|
|
- }
|
|
|
- </select>
|
|
|
- }
|
|
|
+ <div class="input-group mb-5">
|
|
|
+ <span class="input-group-text">
|
|
|
+ <i class="bx bx-microchip"></i>
|
|
|
+ </span>
|
|
|
+ <InputNumber @bind-Value="Model.Memory" class="form-control"></InputNumber>
|
|
|
+ <span class="input-group-text">
|
|
|
+ MB
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <label class="form-label">
|
|
|
+ <TL>Disk</TL>
|
|
|
+ </label>
|
|
|
+ <div class="input-group mb-5">
|
|
|
+ <span class="input-group-text">
|
|
|
+ <i class="bx bx-hdd"></i>
|
|
|
+ </span>
|
|
|
+ <InputNumber @bind-Value="Model.Disk" class="form-control"></InputNumber>
|
|
|
+ <span class="input-group-text">
|
|
|
+ MB
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="row mb-5">
|
|
|
+ <div class="card card-body p-10">
|
|
|
+ <label class="form-label">
|
|
|
+ <TL>Image</TL>
|
|
|
+ </label>
|
|
|
+ <div class="mb-5">
|
|
|
+ <SmartSelect TField="Image" @bind-Value="Model.Image" Items="Images" DisplayField="@(x => x.Name)" OnChange="OnChange"></SmartSelect>
|
|
|
+ </div>
|
|
|
+ @if (Model.Image != null)
|
|
|
+ {
|
|
|
+ <label class="form-label">
|
|
|
+ <TL>Override startup</TL>
|
|
|
+ </label>
|
|
|
+ <div class="input-group mb-5">
|
|
|
+ <span class="input-group-text">
|
|
|
+ <i class="bx bx-terminal"></i>
|
|
|
+ </span>
|
|
|
+ <InputText @bind-Value="Model.OverrideStartup" type="text" class="form-control" placeholder="@(Model.Image.Startup)"></InputText>
|
|
|
+ </div>
|
|
|
+ <label class="form-label">
|
|
|
+ <TL>Docker image</TL>
|
|
|
+ </label>
|
|
|
+ <InputSelect TValue="int" @bind-Value="Model.DockerImageIndex" class="form-control">
|
|
|
+ @foreach (var image in Model.Image.DockerImages)
|
|
|
+ {
|
|
|
+ <option value="@(Model.Image.DockerImages.IndexOf(image))">@(image.Name)</option>
|
|
|
+ }
|
|
|
+ </InputSelect>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- <div class="row mb-5">
|
|
|
- <div class="card card-body">
|
|
|
- @if (Image != null)
|
|
|
- {
|
|
|
- <div class="mt-9 row d-flex">
|
|
|
- @foreach (var vars in ServerVariables.Chunk(4))
|
|
|
- {
|
|
|
- <div class="row mb-3">
|
|
|
- @foreach (var variable in vars)
|
|
|
- {
|
|
|
- <div class="col">
|
|
|
- <div class="card card-body">
|
|
|
- <label class="form-label">
|
|
|
- <TL>Name</TL>
|
|
|
- </label>
|
|
|
- <div class="input-group mb-5">
|
|
|
- <input @bind="variable.Key" type="text" class="form-control disabled" disabled="">
|
|
|
- </div>
|
|
|
- <label class="form-label">
|
|
|
- <TL>Value</TL>
|
|
|
- </label>
|
|
|
- <div class="input-group mb-5">
|
|
|
- <input @bind="variable.Value" type="text" class="form-control">
|
|
|
+ <div class="row mb-5">
|
|
|
+ <div class="card card-body">
|
|
|
+ @if (Model.Image != null)
|
|
|
+ {
|
|
|
+ <div class="mt-9 row d-flex">
|
|
|
+ @foreach (var vars in ServerVariables.Chunk(3))
|
|
|
+ {
|
|
|
+ <div class="row row-cols-3 mb-3">
|
|
|
+ @foreach (var variable in vars)
|
|
|
+ {
|
|
|
+ <div class="col">
|
|
|
+ <div class="card card-body border">
|
|
|
+ <label class="form-label">
|
|
|
+ <TL>Name</TL>
|
|
|
+ </label>
|
|
|
+ <div class="input-group mb-5">
|
|
|
+ <input @bind="variable.Key" type="text" class="form-control disabled" disabled="">
|
|
|
+ </div>
|
|
|
+ <label class="form-label">
|
|
|
+ <TL>Value</TL>
|
|
|
+ </label>
|
|
|
+ <div class="input-group mb-5">
|
|
|
+ <input @bind="variable.Value" type="text" class="form-control">
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- }
|
|
|
- </div>
|
|
|
- }
|
|
|
- </div>
|
|
|
- }
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- <div class="row">
|
|
|
- <div class="card card-body">
|
|
|
- <div class="d-flex justify-content-end">
|
|
|
- <a href="/admin/servers" class="btn btn-danger me-3">
|
|
|
- <TL>Cancel</TL>
|
|
|
- </a>
|
|
|
- <WButton Text="@(SmartTranslateService.Translate("Create"))"
|
|
|
- WorkingText="@(SmartTranslateService.Translate("Creating"))"
|
|
|
- CssClasses="btn-success"
|
|
|
- OnClick="Create">
|
|
|
- </WButton>
|
|
|
+ <div class="row">
|
|
|
+ <div class="card card-body">
|
|
|
+ <div class="d-flex justify-content-end">
|
|
|
+ <a href="/admin/servers" class="btn btn-danger me-3">
|
|
|
+ <TL>Cancel</TL>
|
|
|
+ </a>
|
|
|
+ <button class="btn btn-success" type="submit">
|
|
|
+ <TL>Create</TL>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ </SmartForm>
|
|
|
</LazyLoader>
|
|
|
</OnlyAdmin>
|
|
|
|
|
|
@code
|
|
|
{
|
|
|
+ private ServerDataModel Model = new();
|
|
|
+
|
|
|
private List<Image> Images;
|
|
|
private Node[] Nodes;
|
|
|
private User[] Users;
|
|
|
|
|
|
- private string Name = "";
|
|
|
-
|
|
|
- private int Cpu = 100;
|
|
|
- private int Memory = 4096;
|
|
|
- private int Disk = 10240;
|
|
|
-
|
|
|
- private string OverrideStartup = "";
|
|
|
- private int DockerImageIndex = 0;
|
|
|
-
|
|
|
- private Image? Image;
|
|
|
- private User? User;
|
|
|
-
|
|
|
private ServerVariable[] ServerVariables = Array.Empty<ServerVariable>();
|
|
|
|
|
|
- private int ImageIndex
|
|
|
- {
|
|
|
- get => Image == null ? 0 : Images.IndexOf(Image);
|
|
|
- set
|
|
|
- {
|
|
|
- Image = Images[value];
|
|
|
-
|
|
|
- if (Image == null)
|
|
|
- ServerVariables = Array.Empty<ServerVariable>();
|
|
|
- else
|
|
|
- RebuildVariables();
|
|
|
-
|
|
|
-
|
|
|
- InvokeAsync(StateHasChanged);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
private void RebuildVariables()
|
|
|
{
|
|
|
var list = new List<ServerVariable>();
|
|
|
|
|
|
- foreach (var variable in Image.Variables)
|
|
|
+ if (Model.Image != null)
|
|
|
{
|
|
|
- list.Add(new()
|
|
|
+ foreach (var variable in Model.Image.Variables)
|
|
|
{
|
|
|
- Key = variable.Key,
|
|
|
- Value = variable.DefaultValue
|
|
|
- });
|
|
|
+ list.Add(new()
|
|
|
+ {
|
|
|
+ Key = variable.Key,
|
|
|
+ Value = variable.DefaultValue
|
|
|
+ });
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
ServerVariables = list.ToArray();
|
|
|
}
|
|
|
|
|
|
- private Task<IEnumerable<User>> SearchUsers(string input)
|
|
|
- {
|
|
|
- if (string.IsNullOrEmpty(input))
|
|
|
- {
|
|
|
- return Task.FromResult(Array.Empty<User>().Cast<User>());
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- return Task.FromResult(Users.Where(x => x.Email.ToLower().StartsWith(input)));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
private async Task Load(LazyLoader lazyLoader)
|
|
|
{
|
|
|
await lazyLoader.SetText("Loading images");
|
|
@@ -256,24 +212,18 @@
|
|
|
await lazyLoader.SetText("Loading users");
|
|
|
|
|
|
Users = UserRepository.Get().ToArray();
|
|
|
- User = Users.FirstOrDefault();
|
|
|
-
|
|
|
- Image = Images.FirstOrDefault();
|
|
|
|
|
|
RebuildVariables();
|
|
|
-
|
|
|
- if (Image != null)
|
|
|
- DockerImageIndex = Image.DockerImages.Count - 1;
|
|
|
}
|
|
|
|
|
|
private async Task Create()
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
- await ServerService.Create(Name, Cpu, Memory, Disk, User, Image, null, server =>
|
|
|
+ await ServerService.Create(Model.Name, Model.Cpu, Model.Memory, Model.Disk, Model.Owner, Model.Image, null, server =>
|
|
|
{
|
|
|
- server.OverrideStartup = OverrideStartup;
|
|
|
- server.DockerImageIndex = DockerImageIndex;
|
|
|
+ server.OverrideStartup = Model.OverrideStartup;
|
|
|
+ server.DockerImageIndex = Model.DockerImageIndex;
|
|
|
|
|
|
foreach (var serverVariable in ServerVariables)
|
|
|
{
|
|
@@ -303,4 +253,10 @@
|
|
|
);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ private async Task OnChange()
|
|
|
+ {
|
|
|
+ RebuildVariables();
|
|
|
+ await InvokeAsync(StateHasChanged);
|
|
|
+ }
|
|
|
}
|