Added archive system. Added ml debug menu and related stuff
This commit is contained in:
@@ -13,6 +13,8 @@ public class Server
|
|||||||
public string OverrideStartup { get; set; } = "";
|
public string OverrideStartup { get; set; } = "";
|
||||||
public bool Installing { get; set; } = false;
|
public bool Installing { get; set; } = false;
|
||||||
public bool Suspended { 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<ServerVariable> Variables { get; set; } = new();
|
||||||
public List<ServerBackup> Backups { 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()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int?>("ArchiveId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<int>("Cpu")
|
b.Property<int>("Cpu")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
@@ -511,6 +514,9 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
b.Property<bool>("Installing")
|
b.Property<bool>("Installing")
|
||||||
.HasColumnType("tinyint(1)");
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsArchived")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
b.Property<bool>("IsCleanupException")
|
b.Property<bool>("IsCleanupException")
|
||||||
.HasColumnType("tinyint(1)");
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
@@ -542,6 +548,8 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ArchiveId");
|
||||||
|
|
||||||
b.HasIndex("ImageId");
|
b.HasIndex("ImageId");
|
||||||
|
|
||||||
b.HasIndex("MainAllocationId");
|
b.HasIndex("MainAllocationId");
|
||||||
@@ -935,6 +943,10 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b =>
|
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")
|
b.HasOne("Moonlight.App.Database.Entities.Image", "Image")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("ImageId")
|
.HasForeignKey("ImageId")
|
||||||
@@ -957,6 +969,8 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Archive");
|
||||||
|
|
||||||
b.Navigation("Image");
|
b.Navigation("Image");
|
||||||
|
|
||||||
b.Navigation("MainAllocation");
|
b.Navigation("MainAllocation");
|
||||||
|
|||||||
@@ -112,4 +112,22 @@ public class EventSystem
|
|||||||
|
|
||||||
return Task.CompletedTask;
|
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
|
try
|
||||||
{
|
{
|
||||||
await GetSystemMetrics(node);
|
await GetStatus(node);
|
||||||
|
|
||||||
return true;
|
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;
|
||||||
using Moonlight.App.ApiClients.Wings.Requests;
|
using Moonlight.App.ApiClients.Wings.Requests;
|
||||||
using Moonlight.App.ApiClients.Wings.Resources;
|
using Moonlight.App.ApiClients.Wings.Resources;
|
||||||
@@ -446,4 +447,65 @@ public class ServerService
|
|||||||
|
|
||||||
return await NodeService.IsHostUp(server.Node);
|
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">
|
<span class="ms-1 text-muted">
|
||||||
@if (User.Admin)
|
@if (User.Admin)
|
||||||
{
|
{
|
||||||
<a href="/admin/servers/edit/@(CurrentServer.Id)">@(CurrentServer.Id)</a>
|
<a href="/admin/servers/view/@(CurrentServer.Id)">@(CurrentServer.Id)</a>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@page "/admin/servers/edit/{id:int}"
|
@page "/admin/servers/editx/{id:int}"
|
||||||
|
|
||||||
@using Moonlight.App.Services
|
@using Moonlight.App.Services
|
||||||
@using Moonlight.App.Repositories.Servers
|
@using Moonlight.App.Repositories.Servers
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
</Column>
|
</Column>
|
||||||
<Column TableItem="Server" Title="" Field="@(x => x.Id)" Sortable="false" Filterable="false">
|
<Column TableItem="Server" Title="" Field="@(x => x.Id)" Sortable="false" Filterable="false">
|
||||||
<Template>
|
<Template>
|
||||||
<a href="/admin/servers/edit/@(context.Id)">
|
<a href="/admin/servers/view/@(context.Id)">
|
||||||
@(SmartTranslateService.Translate("Manage"))
|
@(SmartTranslateService.Translate("Manage"))
|
||||||
</a>
|
</a>
|
||||||
</Template>
|
</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 ServerService ServerService
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject DynamicBackgroundService DynamicBackgroundService
|
@inject DynamicBackgroundService DynamicBackgroundService
|
||||||
|
@inject SmartTranslateService SmartTranslateService
|
||||||
|
|
||||||
@implements IDisposable
|
@implements IDisposable
|
||||||
|
|
||||||
@@ -78,6 +79,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</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
|
else
|
||||||
{
|
{
|
||||||
<CascadingValue Value="Console">
|
<CascadingValue Value="Console">
|
||||||
@@ -236,7 +259,7 @@
|
|||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
// ignored
|
// ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CurrentServer != null)
|
if (CurrentServer != null)
|
||||||
@@ -260,7 +283,7 @@
|
|||||||
.Include(x => x.Variables)
|
.Include(x => x.Variables)
|
||||||
.First(x => x.Id == CurrentServer.Image.Id);
|
.First(x => x.Id == CurrentServer.Image.Id);
|
||||||
|
|
||||||
// Live variable migration
|
// Live variable migration
|
||||||
|
|
||||||
foreach (var variable in image.Variables)
|
foreach (var variable in image.Variables)
|
||||||
{
|
{
|
||||||
@@ -276,7 +299,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tags
|
// Tags
|
||||||
|
|
||||||
await lazyLoader.SetText("Requesting tags");
|
await lazyLoader.SetText("Requesting tags");
|
||||||
|
|
||||||
@@ -294,6 +317,13 @@
|
|||||||
return Task.CompletedTask;
|
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))
|
if (string.IsNullOrEmpty(Image.BackgroundImageUrl))
|
||||||
await DynamicBackgroundService.Reset();
|
await DynamicBackgroundService.Reset();
|
||||||
else
|
else
|
||||||
@@ -317,6 +347,7 @@
|
|||||||
if (CurrentServer != null)
|
if (CurrentServer != null)
|
||||||
{
|
{
|
||||||
await Event.Off($"server.{CurrentServer.Uuid}.installComplete", this);
|
await Event.Off($"server.{CurrentServer.Uuid}.installComplete", this);
|
||||||
|
await Event.Off($"server.{CurrentServer.Uuid}.archiveStatusChanged", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Console != null)
|
if (Console != null)
|
||||||
@@ -324,4 +355,9 @@
|
|||||||
Console.Dispose();
|
Console.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task UnArchive()
|
||||||
|
{
|
||||||
|
await ServerService.UnArchiveServer(CurrentServer!);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -12,3 +12,4 @@
|
|||||||
@using Moonlight.Shared.Components.Alerts
|
@using Moonlight.Shared.Components.Alerts
|
||||||
@using Moonlight.Shared.Components.Forms
|
@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 |
Reference in New Issue
Block a user