Merge pull request #156 from Moonlight-Panel/AddServerBackgroundImage
Add dynamic background images for servers
This commit is contained in:
@@ -20,4 +20,5 @@ public class Image
|
|||||||
public List<DockerImage> DockerImages { get; set; } = new();
|
public List<DockerImage> DockerImages { get; set; } = new();
|
||||||
public List<ImageVariable> Variables { get; set; } = new();
|
public List<ImageVariable> Variables { get; set; } = new();
|
||||||
public string TagsJson { get; set; } = "";
|
public string TagsJson { get; set; } = "";
|
||||||
|
public string BackgroundImageUrl { get; set; } = "";
|
||||||
}
|
}
|
||||||
1056
Moonlight/App/Database/Migrations/20230609202138_AddBackgroundImageUrlImage.Designer.cs
generated
Normal file
1056
Moonlight/App/Database/Migrations/20230609202138_AddBackgroundImageUrlImage.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Moonlight.App.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddBackgroundImageUrlImage : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "BackgroundImageUrl",
|
||||||
|
table: "Images",
|
||||||
|
type: "longtext",
|
||||||
|
nullable: false)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "BackgroundImageUrl",
|
||||||
|
table: "Images");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -132,6 +132,10 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
b.Property<int>("Allocations")
|
b.Property<int>("Allocations")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("BackgroundImageUrl")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
b.Property<string>("ConfigFiles")
|
b.Property<string>("ConfigFiles")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
|||||||
@@ -47,6 +47,29 @@ public class ResourcesController : Controller
|
|||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("background/{name}")]
|
||||||
|
public async Task<ActionResult> GetBackground([FromRoute] string name)
|
||||||
|
{
|
||||||
|
if (name.Contains(".."))
|
||||||
|
{
|
||||||
|
await SecurityLogService.Log(SecurityLogType.PathTransversal, x =>
|
||||||
|
{
|
||||||
|
x.Add<string>(name);
|
||||||
|
});
|
||||||
|
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (System.IO.File.Exists(PathBuilder.File("storage", "resources", "public", "background", name)))
|
||||||
|
{
|
||||||
|
var fs = new FileStream(PathBuilder.File("storage", "resources", "public", "background", name), FileMode.Open);
|
||||||
|
|
||||||
|
return File(fs, MimeTypes.GetMimeType(name), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet("bucket/{bucket}/{name}")]
|
[HttpGet("bucket/{bucket}/{name}")]
|
||||||
public async Task<ActionResult> GetBucket([FromRoute] string bucket, [FromRoute] string name)
|
public async Task<ActionResult> GetBucket([FromRoute] string bucket, [FromRoute] string name)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ public class ResourceService
|
|||||||
return $"{AppUrl}/api/moonlight/resources/images/{name}";
|
return $"{AppUrl}/api/moonlight/resources/images/{name}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string BackgroundImage(string name)
|
||||||
|
{
|
||||||
|
return $"{AppUrl}/api/moonlight/resources/background/{name}";
|
||||||
|
}
|
||||||
|
|
||||||
public string Avatar(User user)
|
public string Avatar(User user)
|
||||||
{
|
{
|
||||||
return $"{AppUrl}/api/moonlight/avatar/{user.Id}";
|
return $"{AppUrl}/api/moonlight/avatar/{user.Id}";
|
||||||
|
|||||||
39
Moonlight/App/Services/Sessions/DynamicBackgroundService.cs
Normal file
39
Moonlight/App/Services/Sessions/DynamicBackgroundService.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using Logging.Net;
|
||||||
|
using Moonlight.App.Services.Files;
|
||||||
|
|
||||||
|
namespace Moonlight.App.Services.Sessions;
|
||||||
|
|
||||||
|
public class DynamicBackgroundService
|
||||||
|
{
|
||||||
|
public EventHandler OnBackgroundImageChanged { get; set; }
|
||||||
|
public string BackgroundImageUrl { get; private set; }
|
||||||
|
private string DefaultBackgroundImageUrl;
|
||||||
|
|
||||||
|
public DynamicBackgroundService(ResourceService resourceService)
|
||||||
|
{
|
||||||
|
DefaultBackgroundImageUrl = resourceService.BackgroundImage("main.jpg");
|
||||||
|
BackgroundImageUrl = DefaultBackgroundImageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task Change(string url)
|
||||||
|
{
|
||||||
|
if(BackgroundImageUrl == url) // Prevent unnecessary updates
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
BackgroundImageUrl = url;
|
||||||
|
OnBackgroundImageChanged?.Invoke(this, null!);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task Reset()
|
||||||
|
{
|
||||||
|
if(BackgroundImageUrl == DefaultBackgroundImageUrl) // Prevent unnecessary updates
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
BackgroundImageUrl = DefaultBackgroundImageUrl;
|
||||||
|
OnBackgroundImageChanged?.Invoke(this, null!);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -74,6 +74,7 @@
|
|||||||
<Folder Include="App\ApiClients\CloudPanel\Resources\" />
|
<Folder Include="App\ApiClients\CloudPanel\Resources\" />
|
||||||
<Folder Include="App\Http\Middleware" />
|
<Folder Include="App\Http\Middleware" />
|
||||||
<Folder Include="storage\backups\" />
|
<Folder Include="storage\backups\" />
|
||||||
|
<Folder Include="storage\resources\public\background\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ namespace Moonlight
|
|||||||
builder.Services.AddScoped<ReCaptchaService>();
|
builder.Services.AddScoped<ReCaptchaService>();
|
||||||
builder.Services.AddScoped<IpBanService>();
|
builder.Services.AddScoped<IpBanService>();
|
||||||
builder.Services.AddSingleton<OAuth2Service>();
|
builder.Services.AddSingleton<OAuth2Service>();
|
||||||
|
builder.Services.AddScoped<DynamicBackgroundService>();
|
||||||
|
|
||||||
builder.Services.AddScoped<SubscriptionService>();
|
builder.Services.AddScoped<SubscriptionService>();
|
||||||
builder.Services.AddScoped<SubscriptionAdminService>();
|
builder.Services.AddScoped<SubscriptionAdminService>();
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
@inject ToastService ToastService
|
@inject ToastService ToastService
|
||||||
@inject SmartTranslateService SmartTranslateService
|
@inject SmartTranslateService SmartTranslateService
|
||||||
@inject IpBanService IpBanService
|
@inject IpBanService IpBanService
|
||||||
|
@inject DynamicBackgroundService DynamicBackgroundService
|
||||||
|
|
||||||
<GlobalErrorBoundary>
|
<GlobalErrorBoundary>
|
||||||
@{
|
@{
|
||||||
@@ -56,7 +57,7 @@
|
|||||||
<Sidebar></Sidebar>
|
<Sidebar></Sidebar>
|
||||||
<div class="app-main flex-column flex-row-fluid" id="kt_app_main">
|
<div class="app-main flex-column flex-row-fluid" id="kt_app_main">
|
||||||
<div class="d-flex flex-column flex-column-fluid">
|
<div class="d-flex flex-column flex-column-fluid">
|
||||||
<div id="kt_app_content" class="app-content flex-column-fluid">
|
<div id="kt_app_content" class="app-content flex-column-fluid" style="background-position: center; background-size: cover; background-repeat: no-repeat; background-attachment: fixed; background-image: url('@(DynamicBackgroundService.BackgroundImageUrl)')">
|
||||||
<div id="kt_app_content_container" class="app-container container-fluid">
|
<div id="kt_app_content_container" class="app-container container-fluid">
|
||||||
<div class="mt-10">
|
<div class="mt-10">
|
||||||
<SoftErrorBoundary>
|
<SoftErrorBoundary>
|
||||||
@@ -189,6 +190,11 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
DynamicBackgroundService.OnBackgroundImageChanged += async (_, _) =>
|
||||||
|
{
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
};
|
||||||
|
|
||||||
IsIpBanned = await IpBanService.IsBanned();
|
IsIpBanned = await IpBanService.IsBanned();
|
||||||
|
|
||||||
if(IsIpBanned)
|
if(IsIpBanned)
|
||||||
@@ -211,7 +217,13 @@
|
|||||||
|
|
||||||
await SessionService.Register();
|
await SessionService.Register();
|
||||||
|
|
||||||
NavigationManager.LocationChanged += (sender, args) => { SessionService.Refresh(); };
|
NavigationManager.LocationChanged += async (_, _) =>
|
||||||
|
{
|
||||||
|
SessionService.Refresh();
|
||||||
|
|
||||||
|
if (!NavigationManager.Uri.Contains("/server/"))
|
||||||
|
await DynamicBackgroundService.Reset();
|
||||||
|
};
|
||||||
|
|
||||||
if (User != null)
|
if (User != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,240 +13,250 @@
|
|||||||
@inject FileDownloadService FileDownloadService
|
@inject FileDownloadService FileDownloadService
|
||||||
|
|
||||||
<OnlyAdmin>
|
<OnlyAdmin>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<LazyLoader @ref="LazyLoader" Load="Load">
|
<LazyLoader @ref="LazyLoader" Load="Load">
|
||||||
@if (Image == null)
|
@if (Image == null)
|
||||||
{
|
{
|
||||||
<div class="alert alert-danger">
|
<div class="alert alert-danger">
|
||||||
<TL>No image with this id found</TL>
|
<TL>No image with this id found</TL>
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xl-6 mb-5 mb-xl-10">
|
|
||||||
<div class="card card-body">
|
|
||||||
<div class="mb-10">
|
|
||||||
<label class="form-label">
|
|
||||||
<TL>Name</TL>
|
|
||||||
</label>
|
|
||||||
<input @bind="Image.Name" type="text" class="form-control">
|
|
||||||
</div>
|
|
||||||
<div class="mb-10">
|
|
||||||
<label class="form-label">
|
|
||||||
<TL>Description</TL>
|
|
||||||
</label>
|
|
||||||
<textarea @bind="Image.Description" type="text" class="form-control"></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-xl-6 mb-5 mb-xl-10">
|
|
||||||
<div class="card card-body">
|
|
||||||
<label class="form-label">
|
|
||||||
<TL>Tags</TL>
|
|
||||||
</label>
|
|
||||||
<div class="input-group mb-5">
|
|
||||||
<input @bind="AddTagName" type="text" class="form-control" placeholder="@(SmartTranslateService.Translate("Enter tag name"))">
|
|
||||||
<button @onclick="AddTag" class="btn btn-primary">
|
|
||||||
<TL>Add</TL>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
@if (Tags.Any())
|
|
||||||
{
|
|
||||||
<div class="row">
|
|
||||||
@foreach (var tag in Tags)
|
|
||||||
{
|
|
||||||
<button @onclick="() => RemoveTag(tag)" class="col m-3 btn btn-outline-primary mw-25">
|
|
||||||
@(tag)
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<div class="alert alert-primary">
|
|
||||||
<TL>No tags found</TL>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xl-6 mb-5 mb-xl-10">
|
|
||||||
<div class="card card-body">
|
|
||||||
<label class="form-label">
|
|
||||||
<TL>Docker images</TL>
|
|
||||||
</label>
|
|
||||||
<div class="input-group mb-5">
|
|
||||||
<input @bind="NewDockerImage.Name" type="text" class="form-control" placeholder="@(SmartTranslateService.Translate("Enter docker image name"))">
|
|
||||||
<button @onclick="AddDockerImage" class="btn btn-primary">
|
|
||||||
<TL>Add</TL>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
@if (Image.DockerImages.Any())
|
|
||||||
{
|
|
||||||
<div class="row">
|
|
||||||
@foreach (var imageDocker in Image.DockerImages)
|
|
||||||
{
|
|
||||||
<button @onclick="() => RemoveDockerImage(imageDocker)" class="col m-3 btn btn-outline-primary mw-25">
|
|
||||||
@(imageDocker.Name)
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<div class="alert alert-primary">
|
|
||||||
<TL>No docker images found</TL>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-xl-6 mb-5 mb-xl-10">
|
|
||||||
<div class="card card-body">
|
|
||||||
<div class="mb-10">
|
|
||||||
<label class="form-label">
|
|
||||||
<TL>Default image</TL>
|
|
||||||
</label>
|
|
||||||
<select @bind="DefaultImageIndex" class="form-select">
|
|
||||||
@foreach (var image in Image.DockerImages)
|
|
||||||
{
|
|
||||||
<option value="@(image.Id)">@(image.Name)</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="mb-10">
|
|
||||||
<label class="form-label">
|
|
||||||
<TL>Allocations</TL>
|
|
||||||
</label>
|
|
||||||
<input @bind="Image.Allocations" type="number" class="form-control">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mx-0">
|
|
||||||
<div class="card card-body">
|
|
||||||
<div class="mb-10">
|
|
||||||
<label class="form-label">
|
|
||||||
<TL>Startup command</TL>
|
|
||||||
</label>
|
|
||||||
<input @bind="Image.Startup" type="text" class="form-control">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xl-6 mb-5 mb-xl-10">
|
|
||||||
<div class="mb-10">
|
|
||||||
<label class="form-label">
|
|
||||||
<TL>Install container</TL>
|
|
||||||
</label>
|
|
||||||
<input @bind="Image.InstallDockerImage" type="text" class="form-control">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-xl-6 mb-5 mb-xl-10">
|
|
||||||
<div class="mb-10">
|
|
||||||
<label class="form-label">
|
|
||||||
<TL>Install entry</TL>
|
|
||||||
</label>
|
|
||||||
<input @bind="Image.InstallEntrypoint" type="text" class="form-control">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card card-flush">
|
|
||||||
<FileEditor @ref="Editor" Language="shell" InitialData="@(Image.InstallScript)" HideControls="true"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row my-8">
|
|
||||||
<div class="col-xl-6 mb-5 mb-xl-10">
|
|
||||||
<div class="card card-body">
|
|
||||||
<div class="mb-10">
|
|
||||||
<label class="form-label">
|
|
||||||
<TL>Configuration files</TL>
|
|
||||||
</label>
|
|
||||||
<textarea @bind="Image.ConfigFiles" class="form-control"></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-xl-6 mb-5 mb-xl-10">
|
|
||||||
<div class="card card-body">
|
|
||||||
<div class="mb-10">
|
|
||||||
<label class="form-label">
|
|
||||||
<TL>Startup detection</TL>
|
|
||||||
</label>
|
|
||||||
<input @bind="Image.StartupDetection" type="text" class="form-control">
|
|
||||||
</div>
|
|
||||||
<div class="mb-10">
|
|
||||||
<label class="form-label">
|
|
||||||
<TL>Stop command</TL>
|
|
||||||
</label>
|
|
||||||
<input @bind="Image.StopCommand" type="text" class="form-control">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row my-6">
|
|
||||||
<div class="card card-body">
|
|
||||||
<div class="input-group mb-5">
|
|
||||||
<input type="text" @bind="ImageVariable.Key" placeholder="@(SmartTranslateService.Translate("Key"))" class="form-control">
|
|
||||||
<input type="text" @bind="ImageVariable.DefaultValue" placeholder="@(SmartTranslateService.Translate("Default value"))" class="form-control">
|
|
||||||
<button @onclick="AddVariable" class="btn btn-primary">
|
|
||||||
<TL>Add</TL>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
@if (Image!.Variables.Any())
|
|
||||||
{
|
|
||||||
<div class="row">
|
|
||||||
@foreach (var variable in Image!.Variables)
|
|
||||||
{
|
|
||||||
<div class="input-group mb-3">
|
|
||||||
<input type="text" @bind="variable.Key" placeholder="@(SmartTranslateService.Translate("Key"))" class="form-control">
|
|
||||||
<input type="text" @bind="variable.DefaultValue" placeholder="@(SmartTranslateService.Translate("Default value"))" class="form-control">
|
|
||||||
<button @onclick="() => RemoveVariable(variable)" class="btn btn-danger">
|
|
||||||
<TL>Remove</TL>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<div class="alert alert-primary">
|
|
||||||
<TL>No variables found</TL>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="card card-body">
|
|
||||||
<div class="d-flex justify-content-end">
|
|
||||||
<a href="/admin/servers/images" class="btn btn-danger me-3">
|
|
||||||
<TL>Cancel</TL>
|
|
||||||
</a>
|
|
||||||
<WButton Text="@(SmartTranslateService.Translate("Export"))"
|
|
||||||
WorkingText="@(SmartTranslateService.Translate("Exporting"))"
|
|
||||||
CssClasses="btn-primary me-3"
|
|
||||||
OnClick="Export">
|
|
||||||
</WButton>
|
|
||||||
<WButton Text="@(SmartTranslateService.Translate("Save"))"
|
|
||||||
WorkingText="@(SmartTranslateService.Translate("Saving"))"
|
|
||||||
CssClasses="btn-success"
|
|
||||||
OnClick="Save">
|
|
||||||
</WButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</LazyLoader>
|
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-6 mb-5 mb-xl-10">
|
||||||
|
<div class="card card-body">
|
||||||
|
<div class="mb-10">
|
||||||
|
<label class="form-label">
|
||||||
|
<TL>Name</TL>
|
||||||
|
</label>
|
||||||
|
<input @bind="Image.Name" type="text" class="form-control">
|
||||||
|
</div>
|
||||||
|
<div class="mb-10">
|
||||||
|
<label class="form-label">
|
||||||
|
<TL>Description</TL>
|
||||||
|
</label>
|
||||||
|
<textarea @bind="Image.Description" type="text" class="form-control"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="mb-10">
|
||||||
|
<label class="form-label">
|
||||||
|
<TL>Background image url</TL>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
@bind="Image.BackgroundImageUrl"
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="@(SmartTranslateService.Translate("Leave empty for the default background image"))">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-xl-6 mb-5 mb-xl-10">
|
||||||
|
<div class="card card-body">
|
||||||
|
<label class="form-label">
|
||||||
|
<TL>Tags</TL>
|
||||||
|
</label>
|
||||||
|
<div class="input-group mb-5">
|
||||||
|
<input @bind="AddTagName" type="text" class="form-control" placeholder="@(SmartTranslateService.Translate("Enter tag name"))">
|
||||||
|
<button @onclick="AddTag" class="btn btn-primary">
|
||||||
|
<TL>Add</TL>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
@if (Tags.Any())
|
||||||
|
{
|
||||||
|
<div class="row">
|
||||||
|
@foreach (var tag in Tags)
|
||||||
|
{
|
||||||
|
<button @onclick="() => RemoveTag(tag)" class="col m-3 btn btn-outline-primary mw-25">
|
||||||
|
@(tag)
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="alert alert-primary">
|
||||||
|
<TL>No tags found</TL>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-6 mb-5 mb-xl-10">
|
||||||
|
<div class="card card-body">
|
||||||
|
<label class="form-label">
|
||||||
|
<TL>Docker images</TL>
|
||||||
|
</label>
|
||||||
|
<div class="input-group mb-5">
|
||||||
|
<input @bind="NewDockerImage.Name" type="text" class="form-control" placeholder="@(SmartTranslateService.Translate("Enter docker image name"))">
|
||||||
|
<button @onclick="AddDockerImage" class="btn btn-primary">
|
||||||
|
<TL>Add</TL>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
@if (Image.DockerImages.Any())
|
||||||
|
{
|
||||||
|
<div class="row">
|
||||||
|
@foreach (var imageDocker in Image.DockerImages)
|
||||||
|
{
|
||||||
|
<button @onclick="() => RemoveDockerImage(imageDocker)" class="col m-3 btn btn-outline-primary mw-25">
|
||||||
|
@(imageDocker.Name)
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="alert alert-primary">
|
||||||
|
<TL>No docker images found</TL>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-xl-6 mb-5 mb-xl-10">
|
||||||
|
<div class="card card-body">
|
||||||
|
<div class="mb-10">
|
||||||
|
<label class="form-label">
|
||||||
|
<TL>Default image</TL>
|
||||||
|
</label>
|
||||||
|
<select @bind="DefaultImageIndex" class="form-select">
|
||||||
|
@foreach (var image in Image.DockerImages)
|
||||||
|
{
|
||||||
|
<option value="@(image.Id)">@(image.Name)</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-10">
|
||||||
|
<label class="form-label">
|
||||||
|
<TL>Allocations</TL>
|
||||||
|
</label>
|
||||||
|
<input @bind="Image.Allocations" type="number" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mx-0">
|
||||||
|
<div class="card card-body">
|
||||||
|
<div class="mb-10">
|
||||||
|
<label class="form-label">
|
||||||
|
<TL>Startup command</TL>
|
||||||
|
</label>
|
||||||
|
<input @bind="Image.Startup" type="text" class="form-control">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-6 mb-5 mb-xl-10">
|
||||||
|
<div class="mb-10">
|
||||||
|
<label class="form-label">
|
||||||
|
<TL>Install container</TL>
|
||||||
|
</label>
|
||||||
|
<input @bind="Image.InstallDockerImage" type="text" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-xl-6 mb-5 mb-xl-10">
|
||||||
|
<div class="mb-10">
|
||||||
|
<label class="form-label">
|
||||||
|
<TL>Install entry</TL>
|
||||||
|
</label>
|
||||||
|
<input @bind="Image.InstallEntrypoint" type="text" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-flush">
|
||||||
|
<FileEditor @ref="Editor" Language="shell" InitialData="@(Image.InstallScript)" HideControls="true"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row my-8">
|
||||||
|
<div class="col-xl-6 mb-5 mb-xl-10">
|
||||||
|
<div class="card card-body">
|
||||||
|
<div class="mb-10">
|
||||||
|
<label class="form-label">
|
||||||
|
<TL>Configuration files</TL>
|
||||||
|
</label>
|
||||||
|
<textarea @bind="Image.ConfigFiles" class="form-control"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-xl-6 mb-5 mb-xl-10">
|
||||||
|
<div class="card card-body">
|
||||||
|
<div class="mb-10">
|
||||||
|
<label class="form-label">
|
||||||
|
<TL>Startup detection</TL>
|
||||||
|
</label>
|
||||||
|
<input @bind="Image.StartupDetection" type="text" class="form-control">
|
||||||
|
</div>
|
||||||
|
<div class="mb-10">
|
||||||
|
<label class="form-label">
|
||||||
|
<TL>Stop command</TL>
|
||||||
|
</label>
|
||||||
|
<input @bind="Image.StopCommand" type="text" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row my-6">
|
||||||
|
<div class="card card-body">
|
||||||
|
<div class="input-group mb-5">
|
||||||
|
<input type="text" @bind="ImageVariable.Key" placeholder="@(SmartTranslateService.Translate("Key"))" class="form-control">
|
||||||
|
<input type="text" @bind="ImageVariable.DefaultValue" placeholder="@(SmartTranslateService.Translate("Default value"))" class="form-control">
|
||||||
|
<button @onclick="AddVariable" class="btn btn-primary">
|
||||||
|
<TL>Add</TL>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
@if (Image!.Variables.Any())
|
||||||
|
{
|
||||||
|
<div class="row">
|
||||||
|
@foreach (var variable in Image!.Variables)
|
||||||
|
{
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<input type="text" @bind="variable.Key" placeholder="@(SmartTranslateService.Translate("Key"))" class="form-control">
|
||||||
|
<input type="text" @bind="variable.DefaultValue" placeholder="@(SmartTranslateService.Translate("Default value"))" class="form-control">
|
||||||
|
<button @onclick="() => RemoveVariable(variable)" class="btn btn-danger">
|
||||||
|
<TL>Remove</TL>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="alert alert-primary">
|
||||||
|
<TL>No variables found</TL>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="card card-body">
|
||||||
|
<div class="d-flex justify-content-end">
|
||||||
|
<a href="/admin/servers/images" class="btn btn-danger me-3">
|
||||||
|
<TL>Cancel</TL>
|
||||||
|
</a>
|
||||||
|
<WButton Text="@(SmartTranslateService.Translate("Export"))"
|
||||||
|
WorkingText="@(SmartTranslateService.Translate("Exporting"))"
|
||||||
|
CssClasses="btn-primary me-3"
|
||||||
|
OnClick="Export">
|
||||||
|
</WButton>
|
||||||
|
<WButton Text="@(SmartTranslateService.Translate("Save"))"
|
||||||
|
WorkingText="@(SmartTranslateService.Translate("Saving"))"
|
||||||
|
CssClasses="btn-success"
|
||||||
|
OnClick="Save">
|
||||||
|
</WButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</LazyLoader>
|
||||||
|
</div>
|
||||||
</OnlyAdmin>
|
</OnlyAdmin>
|
||||||
|
|
||||||
@code
|
@code
|
||||||
|
|||||||
@@ -125,111 +125,115 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--d-flex flex-row mb-5-->
|
<div class="row">
|
||||||
<div class="card mb-5">
|
<div class="col">
|
||||||
<div class="card-header card-header-stretch">
|
<div class="card mb-5">
|
||||||
<div class="card-title d-flex align-items-center">
|
<div class="card-header card-header-stretch">
|
||||||
<h3 class="fw-bold m-0 text-gray-800">
|
<div class="card-title d-flex align-items-center">
|
||||||
<TL>Create something new</TL>
|
<h3 class="fw-bold m-0 text-gray-800">
|
||||||
</h3>
|
<TL>Create something new</TL>
|
||||||
</div>
|
</h3>
|
||||||
</div>
|
|
||||||
<div class="card-body pt-3">
|
|
||||||
<div class="flex-row">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<div class="symbol symbol-50px me-3">
|
|
||||||
<i class="bx bx-md bx-server"></i>
|
|
||||||
</div>
|
|
||||||
<div class="d-flex justify-content-start flex-column">
|
|
||||||
<a href="/servers/create" class="text-gray-800 text-hover-primary mb-1 fs-5">
|
|
||||||
<TL>Create a gameserver</TL>
|
|
||||||
</a>
|
|
||||||
<span class="text-gray-400 fw-semibold d-block fs-6">
|
|
||||||
<TL>A new gameserver in just a few minutes</TL>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="separator mb-2 mt-2"></div>
|
<div class="card-body pt-3">
|
||||||
<div class="d-flex align-items-center">
|
<div class="flex-row">
|
||||||
<div class="symbol symbol-50px me-3">
|
<div class="d-flex align-items-center">
|
||||||
<i class="bx bx-md bx-globe"></i>
|
<div class="symbol symbol-50px me-3">
|
||||||
</div>
|
<i class="bx bx-md bx-server"></i>
|
||||||
<div class="d-flex justify-content-start flex-column">
|
</div>
|
||||||
<a href="/webspaces/create" class="text-gray-800 text-hover-primary mb-1 fs-5">
|
<div class="d-flex justify-content-start flex-column">
|
||||||
<TL>Create a webspace</TL>
|
<a href="/servers/create" class="text-gray-800 text-hover-primary mb-1 fs-5">
|
||||||
</a>
|
<TL>Create a gameserver</TL>
|
||||||
<span class="text-gray-400 fw-semibold d-block fs-6">
|
</a>
|
||||||
<TL>Make your own websites with a webspace</TL>
|
<span class="text-gray-400 fw-semibold d-block fs-6">
|
||||||
</span>
|
<TL>A new gameserver in just a few minutes</TL>
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="separator mb-2 mt-2"></div>
|
</div>
|
||||||
<div class="d-flex align-items-center">
|
<div class="separator mb-2 mt-2"></div>
|
||||||
<div class="symbol symbol-50px me-3">
|
<div class="d-flex align-items-center">
|
||||||
<i class="bx bx-md bx-purchase-tag"></i>
|
<div class="symbol symbol-50px me-3">
|
||||||
</div>
|
<i class="bx bx-md bx-globe"></i>
|
||||||
<div class="d-flex justify-content-start flex-column">
|
</div>
|
||||||
<a href="/domains/create" class="text-gray-800 text-hover-primary mb-1 fs-5">
|
<div class="d-flex justify-content-start flex-column">
|
||||||
<TL>Create a domain</TL>
|
<a href="/webspaces/create" class="text-gray-800 text-hover-primary mb-1 fs-5">
|
||||||
</a>
|
<TL>Create a webspace</TL>
|
||||||
<span class="text-gray-400 fw-semibold d-block fs-6">
|
</a>
|
||||||
<TL>Make your servvices accessible throught your own domain</TL>
|
<span class="text-gray-400 fw-semibold d-block fs-6">
|
||||||
</span>
|
<TL>Make your own websites with a webspace</TL>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="separator mb-2 mt-2"></div>
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<div class="symbol symbol-50px me-3">
|
||||||
|
<i class="bx bx-md bx-purchase-tag"></i>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-start flex-column">
|
||||||
|
<a href="/domains/create" class="text-gray-800 text-hover-primary mb-1 fs-5">
|
||||||
|
<TL>Create a domain</TL>
|
||||||
|
</a>
|
||||||
|
<span class="text-gray-400 fw-semibold d-block fs-6">
|
||||||
|
<TL>Make your services accessible through your own domain</TL>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="col">
|
||||||
|
<div class="card mb-5">
|
||||||
<div class="card mb-5">
|
<div class="card-header card-header-stretch">
|
||||||
<div class="card-header card-header-stretch">
|
<div class="card-title d-flex align-items-center">
|
||||||
<div class="card-title d-flex align-items-center">
|
<h3 class="fw-bold m-0 text-gray-800">
|
||||||
<h3 class="fw-bold m-0 text-gray-800">
|
<TL>Manage your services</TL>
|
||||||
<TL>Manage your services</TL>
|
</h3>
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-body pt-3">
|
|
||||||
<div class="flex-row">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<div class="symbol symbol-50px me-3">
|
|
||||||
<i class="bx bx-md bx-server"></i>
|
|
||||||
</div>
|
|
||||||
<div class="d-flex justify-content-start flex-column">
|
|
||||||
<a href="/servers" class="text-gray-800 text-hover-primary mb-1 fs-5">
|
|
||||||
<TL>Manage your gameservers</TL>
|
|
||||||
</a>
|
|
||||||
<span class="text-gray-400 fw-semibold d-block fs-6">
|
|
||||||
<TL>Adjust your gameservers</TL>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="separator mb-2 mt-2"></div>
|
<div class="card-body pt-3">
|
||||||
<div class="d-flex align-items-center">
|
<div class="flex-row">
|
||||||
<div class="symbol symbol-50px me-3">
|
<div class="d-flex align-items-center">
|
||||||
<i class="bx bx-md bx-globe"></i>
|
<div class="symbol symbol-50px me-3">
|
||||||
</div>
|
<i class="bx bx-md bx-server"></i>
|
||||||
<div class="d-flex justify-content-start flex-column">
|
</div>
|
||||||
<a href="/webspaces" class="text-gray-800 text-hover-primary mb-1 fs-5">
|
<div class="d-flex justify-content-start flex-column">
|
||||||
<TL>Manage your webspaces</TL>
|
<a href="/servers" class="text-gray-800 text-hover-primary mb-1 fs-5">
|
||||||
</a>
|
<TL>Manage your gameservers</TL>
|
||||||
<span class="text-gray-400 fw-semibold d-block fs-6">
|
</a>
|
||||||
<TL>Modify the content of your webspaces</TL>
|
<span class="text-gray-400 fw-semibold d-block fs-6">
|
||||||
</span>
|
<TL>Adjust your gameservers</TL>
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="separator mb-2 mt-2"></div>
|
</div>
|
||||||
<div class="d-flex align-items-center">
|
<div class="separator mb-2 mt-2"></div>
|
||||||
<div class="symbol symbol-50px me-3">
|
<div class="d-flex align-items-center">
|
||||||
<i class="bx bx-md bx-purchase-tag"></i>
|
<div class="symbol symbol-50px me-3">
|
||||||
</div>
|
<i class="bx bx-md bx-globe"></i>
|
||||||
<div class="d-flex justify-content-start flex-column">
|
</div>
|
||||||
<a href="/domains" class="text-gray-800 text-hover-primary mb-1 fs-5">
|
<div class="d-flex justify-content-start flex-column">
|
||||||
<TL>Manage your domains</TL>
|
<a href="/webspaces" class="text-gray-800 text-hover-primary mb-1 fs-5">
|
||||||
</a>
|
<TL>Manage your webspaces</TL>
|
||||||
<span class="text-gray-400 fw-semibold d-block fs-6">
|
</a>
|
||||||
<TL>Add, edit and delete dns records</TL>
|
<span class="text-gray-400 fw-semibold d-block fs-6">
|
||||||
</span>
|
<TL>Modify the content of your webspaces</TL>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="separator mb-2 mt-2"></div>
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<div class="symbol symbol-50px me-3">
|
||||||
|
<i class="bx bx-md bx-purchase-tag"></i>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-start flex-column">
|
||||||
|
<a href="/domains" class="text-gray-800 text-hover-primary mb-1 fs-5">
|
||||||
|
<TL>Manage your domains</TL>
|
||||||
|
</a>
|
||||||
|
<span class="text-gray-400 fw-semibold d-block fs-6">
|
||||||
|
<TL>Add, edit and delete dns records</TL>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
@using Moonlight.App.Helpers.Wings.Enums
|
@using Moonlight.App.Helpers.Wings.Enums
|
||||||
@using Moonlight.App.Repositories
|
@using Moonlight.App.Repositories
|
||||||
@using Moonlight.App.Services
|
@using Moonlight.App.Services
|
||||||
|
@using Moonlight.App.Services.Sessions
|
||||||
@using Moonlight.Shared.Components.Xterm
|
@using Moonlight.Shared.Components.Xterm
|
||||||
@using Moonlight.Shared.Components.ServerControl
|
@using Moonlight.Shared.Components.ServerControl
|
||||||
@using Newtonsoft.Json
|
@using Newtonsoft.Json
|
||||||
@@ -20,6 +21,7 @@
|
|||||||
@inject EventSystem Event
|
@inject EventSystem Event
|
||||||
@inject ServerService ServerService
|
@inject ServerService ServerService
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
|
@inject DynamicBackgroundService DynamicBackgroundService
|
||||||
|
|
||||||
@implements IDisposable
|
@implements IDisposable
|
||||||
|
|
||||||
@@ -291,6 +293,11 @@
|
|||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(Image.BackgroundImageUrl))
|
||||||
|
await DynamicBackgroundService.Reset();
|
||||||
|
else
|
||||||
|
await DynamicBackgroundService.Change(Image.BackgroundImageUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
BIN
Moonlight/defaultstorage/resources/public/background/main.jpg
Normal file
BIN
Moonlight/defaultstorage/resources/public/background/main.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
Reference in New Issue
Block a user