Merge pull request #168 from Moonlight-Panel/AddServerArchive
Added archive system. Added ml debug menu and related stuff
This commit is contained in:
commit
0e04942111
25 changed files with 1868 additions and 14 deletions
|
@ -13,6 +13,8 @@ public class Server
|
|||
public string OverrideStartup { get; set; } = "";
|
||||
public bool Installing { get; set; } = false;
|
||||
public bool Suspended { get; set; } = false;
|
||||
public bool IsArchived { get; set; } = false;
|
||||
public ServerBackup? Archive { get; set; } = null;
|
||||
|
||||
public List<ServerVariable> Variables { get; set; } = new();
|
||||
public List<ServerBackup> Backups { get; set; } = new();
|
||||
|
|
1073
Moonlight/App/Database/Migrations/20230614010621_AddedServerAchive.Designer.cs
generated
Normal file
1073
Moonlight/App/Database/Migrations/20230614010621_AddedServerAchive.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,59 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Moonlight.App.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddedServerAchive : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "ArchiveId",
|
||||
table: "Servers",
|
||||
type: "int",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "IsArchived",
|
||||
table: "Servers",
|
||||
type: "tinyint(1)",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Servers_ArchiveId",
|
||||
table: "Servers",
|
||||
column: "ArchiveId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Servers_ServerBackups_ArchiveId",
|
||||
table: "Servers",
|
||||
column: "ArchiveId",
|
||||
principalTable: "ServerBackups",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Servers_ServerBackups_ArchiveId",
|
||||
table: "Servers");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Servers_ArchiveId",
|
||||
table: "Servers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ArchiveId",
|
||||
table: "Servers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "IsArchived",
|
||||
table: "Servers");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -496,6 +496,9 @@ namespace Moonlight.App.Database.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("ArchiveId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Cpu")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
@ -511,6 +514,9 @@ namespace Moonlight.App.Database.Migrations
|
|||
b.Property<bool>("Installing")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("IsArchived")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("IsCleanupException")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
|
@ -542,6 +548,8 @@ namespace Moonlight.App.Database.Migrations
|
|||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.HasIndex("ImageId");
|
||||
|
||||
b.HasIndex("MainAllocationId");
|
||||
|
@ -935,6 +943,10 @@ namespace Moonlight.App.Database.Migrations
|
|||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.App.Database.Entities.ServerBackup", "Archive")
|
||||
.WithMany()
|
||||
.HasForeignKey("ArchiveId");
|
||||
|
||||
b.HasOne("Moonlight.App.Database.Entities.Image", "Image")
|
||||
.WithMany()
|
||||
.HasForeignKey("ImageId")
|
||||
|
@ -957,6 +969,8 @@ namespace Moonlight.App.Database.Migrations
|
|||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Archive");
|
||||
|
||||
b.Navigation("Image");
|
||||
|
||||
b.Navigation("MainAllocation");
|
||||
|
|
|
@ -112,4 +112,22 @@ public class EventSystem
|
|||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<T> WaitForEvent<T>(string id, object handle, Func<T, bool> filter)
|
||||
{
|
||||
var taskCompletionSource = new TaskCompletionSource<T>();
|
||||
|
||||
Func<T, Task> action = async data =>
|
||||
{
|
||||
if (filter.Invoke(data))
|
||||
{
|
||||
taskCompletionSource.SetResult(data);
|
||||
await Off(id, handle);
|
||||
}
|
||||
};
|
||||
|
||||
On<T>(id, handle, action);
|
||||
|
||||
return taskCompletionSource.Task;
|
||||
}
|
||||
}
|
8
Moonlight/App/Models/Forms/ServerImageDataModel.cs
Normal file
8
Moonlight/App/Models/Forms/ServerImageDataModel.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Moonlight.App.Models.Forms;
|
||||
|
||||
public class ServerImageDataModel
|
||||
{
|
||||
public string OverrideStartup { get; set; }
|
||||
|
||||
public int DockerImageIndex { get; set; }
|
||||
}
|
14
Moonlight/App/Models/Forms/ServerOverviewDataModel.cs
Normal file
14
Moonlight/App/Models/Forms/ServerOverviewDataModel.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Moonlight.App.Database.Entities;
|
||||
|
||||
namespace Moonlight.App.Models.Forms;
|
||||
|
||||
public class ServerOverviewDataModel
|
||||
{
|
||||
[Required(ErrorMessage = "You need to enter a name")]
|
||||
[MaxLength(32, ErrorMessage = "The name cannot be longer that 32 characters")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "You need to specify a owner")]
|
||||
public User Owner { get; set; }
|
||||
}
|
15
Moonlight/App/Models/Forms/ServerResourcesDataModel.cs
Normal file
15
Moonlight/App/Models/Forms/ServerResourcesDataModel.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Moonlight.App.Models.Forms;
|
||||
|
||||
public class ServerResourcesDataModel
|
||||
{
|
||||
[Required(ErrorMessage = "You need to specify the cpu cores")]
|
||||
public int Cpu { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "You need to specify the memory")]
|
||||
public long Memory { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "You need to specify the disk")]
|
||||
public long Disk { get; set; }
|
||||
}
|
|
@ -72,7 +72,7 @@ public class NodeService
|
|||
{
|
||||
try
|
||||
{
|
||||
await GetSystemMetrics(node);
|
||||
await GetStatus(node);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Logging.Net;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Moonlight.App.ApiClients.Wings;
|
||||
using Moonlight.App.ApiClients.Wings.Requests;
|
||||
using Moonlight.App.ApiClients.Wings.Resources;
|
||||
|
@ -446,4 +447,65 @@ public class ServerService
|
|||
|
||||
return await NodeService.IsHostUp(server.Node);
|
||||
}
|
||||
|
||||
public async Task ArchiveServer(Server server)
|
||||
{
|
||||
if (server.IsArchived)
|
||||
throw new DisplayException("Unable to archive an already archived server");
|
||||
|
||||
// Archive server
|
||||
|
||||
var backup = await CreateBackup(server);
|
||||
server.IsArchived = true;
|
||||
server.Archive = backup;
|
||||
|
||||
ServerRepository.Update(server);
|
||||
|
||||
await Event.WaitForEvent<ServerBackup>("wings.backups.create", this, x => backup.Id == x.Id);
|
||||
|
||||
// Reset server
|
||||
|
||||
var access = await CreateFileAccess(server, null!);
|
||||
var files = await access.Ls();
|
||||
foreach (var file in files)
|
||||
{
|
||||
try
|
||||
{
|
||||
await access.Delete(file);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
await Event.Emit($"server.{server.Uuid}.archiveStatusChanged", server);
|
||||
}
|
||||
|
||||
public async Task UnArchiveServer(Server s)
|
||||
{
|
||||
if (!s.IsArchived)
|
||||
throw new DisplayException("Unable to unarchive a server which is not archived");
|
||||
|
||||
var server = ServerRepository
|
||||
.Get()
|
||||
.Include(x => x.Archive)
|
||||
.First(x => x.Id == s.Id);
|
||||
|
||||
if (server.Archive == null)
|
||||
throw new DisplayException("Archive from server not found");
|
||||
|
||||
if (!server.Archive.Created)
|
||||
throw new DisplayException("Creating the server archive is in progress");
|
||||
|
||||
await RestoreBackup(server, server.Archive);
|
||||
|
||||
await Event.WaitForEvent<ServerBackup>("wings.backups.restore", this,
|
||||
x => x.Id == server.Archive.Id);
|
||||
|
||||
server.IsArchived = false;
|
||||
ServerRepository.Update(server);
|
||||
|
||||
await Event.Emit($"server.{server.Uuid}.archiveStatusChanged", server);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
@using Moonlight.App.Database.Entities
|
||||
<div class="card mb-5 mb-xl-10">
|
||||
<div class="card-body pt-0 pb-0">
|
||||
<ul class="nav nav-stretch nav-line-tabs nav-line-tabs-2x border-transparent fs-5 fw-bold">
|
||||
<li class="nav-item mt-2">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 0 ? "active" : "")" href="/admin/servers/view/@(Server.Id)">
|
||||
<TL>Overview</TL>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item mt-2">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 1 ? "active" : "")" href="/admin/servers/view/@(Server.Id)/image">
|
||||
<TL>Image</TL>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item mt-2">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 2 ? "active" : "")" href="/admin/servers/view/@(Server.Id)/resources">
|
||||
<TL>Resources</TL>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item mt-2">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 3 ? "active" : "")" href="/admin/servers/view/@(Server.Id)/allocations">
|
||||
<TL>Allocations</TL>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item mt-2">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 4 ? "active" : "")" href="/admin/servers/view/@(Server.Id)/archive">
|
||||
<TL>Archive</TL>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item mt-2">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 5 ? "active" : "")" href="/admin/servers/view/@(Server.Id)/debug">
|
||||
<TL>Debug</TL>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item mt-2">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 6 ? "active" : "")" href="/admin/servers/view/@(Server.Id)/delete">
|
||||
<TL>Delete</TL>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public int Index { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Server Server { get; set; }
|
||||
}
|
20
Moonlight/Shared/Components/Router/Route.razor
Normal file
20
Moonlight/Shared/Components/Router/Route.razor
Normal file
|
@ -0,0 +1,20 @@
|
|||
@{
|
||||
var route = "/" + (Router.Route ?? "");
|
||||
}
|
||||
|
||||
@if (route == Path)
|
||||
{
|
||||
@ChildContent
|
||||
}
|
||||
|
||||
@code
|
||||
{
|
||||
[CascadingParameter]
|
||||
public SmartRouter Router { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Path { get; set; }
|
||||
}
|
12
Moonlight/Shared/Components/Router/SmartRouter.razor
Normal file
12
Moonlight/Shared/Components/Router/SmartRouter.razor
Normal file
|
@ -0,0 +1,12 @@
|
|||
<CascadingValue TValue="SmartRouter" Value="@this">
|
||||
@ChildContent
|
||||
</CascadingValue>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public string? Route { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
}
|
|
@ -57,7 +57,7 @@
|
|||
<span class="ms-1 text-muted">
|
||||
@if (User.Admin)
|
||||
{
|
||||
<a href="/admin/servers/edit/@(CurrentServer.Id)">@(CurrentServer.Id)</a>
|
||||
<a href="/admin/servers/view/@(CurrentServer.Id)">@(CurrentServer.Id)</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@page "/admin/servers/edit/{id:int}"
|
||||
@page "/admin/servers/editx/{id:int}"
|
||||
|
||||
@using Moonlight.App.Services
|
||||
@using Moonlight.App.Repositories.Servers
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
</Column>
|
||||
<Column TableItem="Server" Title="" Field="@(x => x.Id)" Sortable="false" Filterable="false">
|
||||
<Template>
|
||||
<a href="/admin/servers/edit/@(context.Id)">
|
||||
<a href="/admin/servers/view/@(context.Id)">
|
||||
@(SmartTranslateService.Translate("Manage"))
|
||||
</a>
|
||||
</Template>
|
||||
|
|
68
Moonlight/Shared/Views/Admin/Servers/View/Archive.razor
Normal file
68
Moonlight/Shared/Views/Admin/Servers/View/Archive.razor
Normal file
|
@ -0,0 +1,68 @@
|
|||
@using Moonlight.App.Database.Entities
|
||||
@using Moonlight.App.Services
|
||||
@using Moonlight.App.Services.Interop
|
||||
|
||||
@inject ServerService ServerService
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
@inject AlertService AlertService
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
@if (Server.IsArchived)
|
||||
{
|
||||
<span class="fs-5 text-warning"><TL>Server is currently archived</TL></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="fs-5"><TL>Server is currently not archived</TL></span>
|
||||
}
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="text-end">
|
||||
@if (Server.IsArchived)
|
||||
{
|
||||
<WButton Text="@(SmartTranslateService.Translate("Unarchive"))"
|
||||
WorkingText="@(SmartTranslateService.Translate("Unarchiving"))"
|
||||
CssClasses="btn-success"
|
||||
OnClick="UnArchiveServer">
|
||||
</WButton>
|
||||
}
|
||||
else
|
||||
{
|
||||
<WButton Text="@(SmartTranslateService.Translate("Archive"))"
|
||||
WorkingText="@(SmartTranslateService.Translate("Archiving"))"
|
||||
CssClasses="btn-danger"
|
||||
OnClick="ArchiveServer">
|
||||
</WButton>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
[CascadingParameter]
|
||||
public Server Server { get; set; }
|
||||
|
||||
private async Task ArchiveServer()
|
||||
{
|
||||
await ServerService.ArchiveServer(Server);
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
await AlertService.Success(
|
||||
SmartTranslateService.Translate("Successfully archived the server")
|
||||
);
|
||||
}
|
||||
|
||||
private async Task UnArchiveServer()
|
||||
{
|
||||
await ServerService.UnArchiveServer(Server);
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
await AlertService.Success(
|
||||
SmartTranslateService.Translate("Successfully unarchived the server")
|
||||
);
|
||||
}
|
||||
}
|
31
Moonlight/Shared/Views/Admin/Servers/View/Debug.razor
Normal file
31
Moonlight/Shared/Views/Admin/Servers/View/Debug.razor
Normal file
|
@ -0,0 +1,31 @@
|
|||
@using Moonlight.App.Database.Entities
|
||||
@using Moonlight.App.Services
|
||||
|
||||
@inject ServerService ServerService
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<span class="card-title">
|
||||
<TL>Reinstall</TL>
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<WButton Text="@(SmartTranslateService.Translate("Reinstall"))"
|
||||
WorkingText="@(SmartTranslateService.Translate("Reinstalling"))"
|
||||
CssClasses="btn-warning"
|
||||
OnClick="Reinstall">
|
||||
</WButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
[CascadingParameter]
|
||||
public Server Server { get; set; }
|
||||
|
||||
private async Task Reinstall()
|
||||
{
|
||||
await ServerService.Reinstall(Server!);
|
||||
}
|
||||
}
|
111
Moonlight/Shared/Views/Admin/Servers/View/Image.razor
Normal file
111
Moonlight/Shared/Views/Admin/Servers/View/Image.razor
Normal file
|
@ -0,0 +1,111 @@
|
|||
@using Moonlight.App.Database.Entities
|
||||
@using Moonlight.App.Repositories
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Moonlight.App.Models.Forms
|
||||
@using Mappy.Net
|
||||
@using Moonlight.App.Services
|
||||
@using Moonlight.App.Services.Interop
|
||||
|
||||
@inject Repository<Moonlight.App.Database.Entities.Image> ImageRepository
|
||||
@inject Repository<Server> ServerRepository
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
@inject ToastService ToastService
|
||||
|
||||
<LazyLoader Load="Load">
|
||||
<SmartForm Model="Model" OnValidSubmit="OnValidSubmit">
|
||||
<div class="card">
|
||||
<div class="card-body p-10">
|
||||
<label class="form-label">
|
||||
<TL>Override startup command</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="@(Server.Image.Startup)"></InputText>
|
||||
</div>
|
||||
<label class="form-label">
|
||||
<TL>Docker image</TL>
|
||||
</label>
|
||||
<select @bind="Model.DockerImageIndex" class="form-select">
|
||||
@foreach (var image in DockerImages)
|
||||
{
|
||||
<option value="@(DockerImages.IndexOf(image))">@(image.Name)</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@foreach (var vars in Server.Variables.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>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="text-end">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<TL>Save changes</TL>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SmartForm>
|
||||
</LazyLoader>
|
||||
|
||||
@code
|
||||
{
|
||||
[CascadingParameter]
|
||||
public Server Server { get; set; }
|
||||
|
||||
private List<DockerImage> DockerImages;
|
||||
private List<Moonlight.App.Database.Entities.Image> Images;
|
||||
|
||||
private ServerImageDataModel Model;
|
||||
|
||||
private Task Load(LazyLoader arg)
|
||||
{
|
||||
Images = ImageRepository
|
||||
.Get()
|
||||
.Include(x => x.Variables)
|
||||
.Include(x => x.DockerImages)
|
||||
.ToList();
|
||||
|
||||
DockerImages = Images
|
||||
.First(x => x.Id == Server.Image.Id).DockerImages
|
||||
.ToList();
|
||||
|
||||
Model = Mapper.Map<ServerImageDataModel>(Server);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task OnValidSubmit()
|
||||
{
|
||||
Server = Mapper.Map(Server, Model);
|
||||
|
||||
ServerRepository.Update(Server);
|
||||
|
||||
await ToastService.Success(
|
||||
SmartTranslateService.Translate("Successfully saved changes")
|
||||
);
|
||||
}
|
||||
}
|
79
Moonlight/Shared/Views/Admin/Servers/View/Index.razor
Normal file
79
Moonlight/Shared/Views/Admin/Servers/View/Index.razor
Normal file
|
@ -0,0 +1,79 @@
|
|||
@page "/admin/servers/view/{Id:int}/{Route?}"
|
||||
|
||||
@using Moonlight.App.Repositories
|
||||
@using Moonlight.App.Database.Entities
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Moonlight.Shared.Components.Navigations
|
||||
|
||||
@inject Repository<Server> ServerRepository
|
||||
|
||||
<OnlyAdmin>
|
||||
<LazyLoader @ref="LazyLoader" Load="Load">
|
||||
@if (Server == null)
|
||||
{
|
||||
<div class="alert alert-danger">
|
||||
<TL>No server with this id found</TL>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<CascadingValue TValue="Server" Value="Server">
|
||||
<SmartRouter Route="@Route">
|
||||
<Route Path="/">
|
||||
<AdminServersViewNavigation Index="0" Server="Server"/>
|
||||
<Overview/>
|
||||
</Route>
|
||||
<Route Path="/image">
|
||||
<AdminServersViewNavigation Index="1" Server="Server"/>
|
||||
<Image/>
|
||||
</Route>
|
||||
<Route Path="/resources">
|
||||
<AdminServersViewNavigation Index="2" Server="Server"/>
|
||||
<Resources/>
|
||||
</Route>
|
||||
<Route Path="/allocations">
|
||||
<AdminServersViewNavigation Index="3" Server="Server"/>
|
||||
</Route>
|
||||
<Route Path="/archive">
|
||||
<AdminServersViewNavigation Index="4" Server="Server"/>
|
||||
<Archive/>
|
||||
</Route>
|
||||
<Route Path="/debug">
|
||||
<AdminServersViewNavigation Index="5" Server="Server"/>
|
||||
<Debug/>
|
||||
</Route>
|
||||
<Route Path="/delete">
|
||||
<AdminServersViewNavigation Index="6" Server="Server"/>
|
||||
</Route>
|
||||
</SmartRouter>
|
||||
</CascadingValue>
|
||||
}
|
||||
</LazyLoader>
|
||||
</OnlyAdmin>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public string? Route { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public int Id { get; set; }
|
||||
|
||||
private LazyLoader LazyLoader;
|
||||
private Server? Server;
|
||||
|
||||
private Task Load(LazyLoader arg)
|
||||
{
|
||||
Server = ServerRepository
|
||||
.Get()
|
||||
.Include(x => x.Image)
|
||||
.Include(x => x.Owner)
|
||||
.Include(x => x.Archive)
|
||||
.Include(x => x.Allocations)
|
||||
.Include(x => x.MainAllocation)
|
||||
.Include(x => x.Variables)
|
||||
.FirstOrDefault(x => x.Id == Id);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
91
Moonlight/Shared/Views/Admin/Servers/View/Overview.razor
Normal file
91
Moonlight/Shared/Views/Admin/Servers/View/Overview.razor
Normal file
|
@ -0,0 +1,91 @@
|
|||
@using Moonlight.App.Repositories
|
||||
@using Moonlight.App.Database.Entities
|
||||
@using Moonlight.App.Models.Forms
|
||||
@using Moonlight.App.Services
|
||||
@using Moonlight.App.Services.Interop
|
||||
@using Mappy.Net
|
||||
|
||||
@inject Repository<User> UserRepository
|
||||
@inject Repository<Server> ServerRepository
|
||||
@inject ToastService ToastService
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
|
||||
<LazyLoader Load="Load">
|
||||
<SmartForm Model="Model" OnValidSubmit="OnValidSubmit">
|
||||
<div class="card">
|
||||
<div class="card-body p-10">
|
||||
<label class="form-label">
|
||||
<TL>Identifier</TL>
|
||||
</label>
|
||||
<div class="input-group mb-5">
|
||||
<span class="input-group-text">
|
||||
<i class="bx bx-id-card"></i>
|
||||
</span>
|
||||
<input type="number" class="form-control disabled" disabled="" value="@(Server.Id)">
|
||||
</div>
|
||||
<label class="form-label">
|
||||
<TL>UuidIdentifier</TL>
|
||||
</label>
|
||||
<div class="input-group mb-5">
|
||||
<span class="input-group-text">
|
||||
<i class="bx bx-id-card"></i>
|
||||
</span>
|
||||
<input type="text" class="form-control disabled" disabled="" value="@(Server.Uuid)">
|
||||
</div>
|
||||
<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 T="User"
|
||||
@bind-Value="Model.Owner"
|
||||
Items="Users"
|
||||
DisplayFunc="@(x => x.Email)"
|
||||
SearchProp="@(x => x.Email)">
|
||||
</SmartDropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="text-end">
|
||||
<button type="submit" class="btn btn-success"><TL>Save changes</TL></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SmartForm>
|
||||
</LazyLoader>
|
||||
|
||||
@code
|
||||
{
|
||||
[CascadingParameter]
|
||||
public Server Server { get; set; }
|
||||
|
||||
private ServerOverviewDataModel Model;
|
||||
private User[] Users;
|
||||
|
||||
private Task Load(LazyLoader arg)
|
||||
{
|
||||
Users = UserRepository.Get().ToArray();
|
||||
|
||||
Model = Mapper.Map<ServerOverviewDataModel>(Server);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task OnValidSubmit()
|
||||
{
|
||||
Server = Mapper.Map(Server, Model);
|
||||
ServerRepository.Update(Server);
|
||||
|
||||
await ToastService.Success(
|
||||
SmartTranslateService.Translate("Successfully saved changes")
|
||||
);
|
||||
}
|
||||
}
|
88
Moonlight/Shared/Views/Admin/Servers/View/Resources.razor
Normal file
88
Moonlight/Shared/Views/Admin/Servers/View/Resources.razor
Normal file
|
@ -0,0 +1,88 @@
|
|||
@using Moonlight.App.Database.Entities
|
||||
@using Moonlight.App.Models.Forms
|
||||
@using Moonlight.App.Repositories
|
||||
@using Moonlight.App.Services
|
||||
@using Moonlight.App.Services.Interop
|
||||
@using Mappy.Net
|
||||
|
||||
@inject Repository<Server> ServerRepository
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
@inject ToastService ToastService
|
||||
|
||||
<LazyLoader Load="Load">
|
||||
<SmartForm Model="Model" OnValidSubmit="OnValidSubmit">
|
||||
<div class="card">
|
||||
<div class="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>
|
||||
<InputNumber @bind-Value="Model.Cpu" type="number" class="form-control"></InputNumber>
|
||||
<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>
|
||||
<InputNumber @bind-Value="Model.Memory" type="number" 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" type="number" class="form-control"></InputNumber>
|
||||
<span class="input-group-text">
|
||||
MB
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="text-end">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<TL>Save changes</TL>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SmartForm>
|
||||
</LazyLoader>
|
||||
|
||||
@code
|
||||
{
|
||||
[CascadingParameter]
|
||||
public Server Server { get; set; }
|
||||
|
||||
private ServerResourcesDataModel Model;
|
||||
|
||||
private Task Load(LazyLoader arg)
|
||||
{
|
||||
Model = Mapper.Map<ServerResourcesDataModel>(Server);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task OnValidSubmit()
|
||||
{
|
||||
Server = Mapper.Map(Server, Model);
|
||||
|
||||
ServerRepository.Update(Server);
|
||||
|
||||
await ToastService.Success(
|
||||
SmartTranslateService.Translate("Successfully saved changes")
|
||||
);
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@
|
|||
@inject ServerService ServerService
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject DynamicBackgroundService DynamicBackgroundService
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
|
||||
@implements IDisposable
|
||||
|
||||
|
@ -78,6 +79,28 @@
|
|||
</div>
|
||||
</div>
|
||||
}
|
||||
else if (CurrentServer.IsArchived)
|
||||
{
|
||||
<div class="d-flex justify-content-center flex-center">
|
||||
<div class="card">
|
||||
<img src="/assets/media/svg/archive.svg" class="card-img-top w-50 mx-auto pt-5" alt="Not found image"/>
|
||||
<div class="card-body text-center">
|
||||
<h1 class="card-title">
|
||||
<TL>Server is currently archived</TL>
|
||||
</h1>
|
||||
<p class="card-text fs-4">
|
||||
<TL>This server is archived. The data of this server is stored as a backup. To restore click the unarchive button an be patient</TL>
|
||||
</p>
|
||||
|
||||
<WButton Text="@(SmartTranslateService.Translate("Unarchive"))"
|
||||
WorkingText="@(SmartTranslateService.Translate("Please wait"))"
|
||||
CssClasses="btn-primary"
|
||||
OnClick="UnArchive">
|
||||
</WButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<CascadingValue Value="Console">
|
||||
|
@ -236,7 +259,7 @@
|
|||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (CurrentServer != null)
|
||||
|
@ -260,8 +283,8 @@
|
|||
.Include(x => x.Variables)
|
||||
.First(x => x.Id == CurrentServer.Image.Id);
|
||||
|
||||
// Live variable migration
|
||||
|
||||
// Live variable migration
|
||||
|
||||
foreach (var variable in image.Variables)
|
||||
{
|
||||
if (!CurrentServer.Variables.Any(x => x.Key == variable.Key))
|
||||
|
@ -271,13 +294,13 @@
|
|||
Key = variable.Key,
|
||||
Value = variable.DefaultValue
|
||||
});
|
||||
|
||||
|
||||
ServerRepository.Update(CurrentServer);
|
||||
}
|
||||
}
|
||||
|
||||
// Tags
|
||||
|
||||
|
||||
// Tags
|
||||
|
||||
await lazyLoader.SetText("Requesting tags");
|
||||
|
||||
Tags = JsonConvert.DeserializeObject<string[]>(image.TagsJson) ?? Array.Empty<string>();
|
||||
|
@ -294,6 +317,13 @@
|
|||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
await Event.On<Server>($"server.{CurrentServer.Uuid}.archiveStatusChanged", this, server =>
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigationManager.Uri, true);
|
||||
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
if (string.IsNullOrEmpty(Image.BackgroundImageUrl))
|
||||
await DynamicBackgroundService.Reset();
|
||||
else
|
||||
|
@ -305,7 +335,7 @@
|
|||
Logger.Debug("Server is null");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task ReconnectConsole()
|
||||
{
|
||||
await Console!.Disconnect();
|
||||
|
@ -317,6 +347,7 @@
|
|||
if (CurrentServer != null)
|
||||
{
|
||||
await Event.Off($"server.{CurrentServer.Uuid}.installComplete", this);
|
||||
await Event.Off($"server.{CurrentServer.Uuid}.archiveStatusChanged", this);
|
||||
}
|
||||
|
||||
if (Console != null)
|
||||
|
@ -324,4 +355,9 @@
|
|||
Console.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UnArchive()
|
||||
{
|
||||
await ServerService.UnArchiveServer(CurrentServer!);
|
||||
}
|
||||
}
|
|
@ -11,4 +11,5 @@
|
|||
@using Moonlight.Shared.Components.StateLogic
|
||||
@using Moonlight.Shared.Components.Alerts
|
||||
@using Moonlight.Shared.Components.Forms
|
||||
@using Moonlight.Shared.Components.Partials
|
||||
@using Moonlight.Shared.Components.Partials
|
||||
@using Moonlight.Shared.Components.Router
|
1
Moonlight/wwwroot/assets/media/svg/archive.svg
Normal file
1
Moonlight/wwwroot/assets/media/svg/archive.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 6.3 KiB |
Loading…
Reference in a new issue