Compare commits
42 commits
v2_AddAllo
...
v2
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a4080cc1b1 | ||
![]() |
4f5a4913d7 | ||
![]() |
c340e48f02 | ||
![]() |
257af8106d | ||
![]() |
6eedc8aba9 | ||
![]() |
cba98bdf6f | ||
![]() |
d2da868b71 | ||
![]() |
685221b454 | ||
![]() |
f12e5f10d5 | ||
![]() |
cc7b4d7daa | ||
![]() |
769c876dc5 | ||
![]() |
ccec79cca7 | ||
![]() |
e79f2199c3 | ||
![]() |
c53d315bd8 | ||
![]() |
0ae9c27d93 | ||
![]() |
2950034a30 | ||
![]() |
158115bb3b | ||
![]() |
56184a8254 | ||
![]() |
c27b1689f3 | ||
![]() |
3c3dd2af92 | ||
![]() |
125260e7ef | ||
![]() |
b608a0779c | ||
![]() |
7225db0bf1 | ||
![]() |
3f0cdff262 | ||
![]() |
3270039a6a | ||
![]() |
fda972a90e | ||
![]() |
45e81c98bf | ||
![]() |
d9dd9bbf4d | ||
![]() |
160de6443b | ||
![]() |
923a3c18b8 | ||
![]() |
8dc37525ce | ||
![]() |
7bd34842fa | ||
![]() |
da8b01bb98 | ||
![]() |
c9fe469f5b | ||
![]() |
406f7cad65 | ||
![]() |
558e237608 | ||
![]() |
b4251a0f1f | ||
![]() |
0234a8e179 | ||
![]() |
efacaa9b86 | ||
![]() |
7c40d999ff | ||
![]() |
99c14693d5 | ||
![]() |
2cf03d4b68 |
47 changed files with 920 additions and 234 deletions
15
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
15
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
|
@ -33,7 +33,7 @@ body:
|
|||
attributes:
|
||||
label: Panel Version
|
||||
description: Version number of your Panel (latest is not a version)
|
||||
placeholder: 1.4.0
|
||||
placeholder: v2 EA
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
@ -42,16 +42,7 @@ body:
|
|||
attributes:
|
||||
label: Daemon Version
|
||||
description: Version number of your Daemon (latest is not a version)
|
||||
placeholder: 1.4.2
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: wings-version
|
||||
attributes:
|
||||
label: Wings Version
|
||||
description: Version number of your Wings (latest is not a version)
|
||||
placeholder: 1.4.2
|
||||
placeholder: v2 EA
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
@ -93,4 +84,4 @@ body:
|
|||
- label: I have provided all relevant details, including the specific game and Docker images I am using if this issue is related to running a server.
|
||||
required: true
|
||||
- label: I have checked in the Discord server and believe this is a bug with the software, and not a configuration issue with my specific system.
|
||||
required: true
|
||||
required: true
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -400,4 +400,5 @@ FodyWeavers.xsd
|
|||
storage/
|
||||
.idea/.idea.Moonlight/.idea/dataSources.xml
|
||||
Moonlight/wwwroot/css/theme.css
|
||||
Moonlight/wwwroot/css/theme.css.map
|
||||
Moonlight/wwwroot/css/theme.css.map
|
||||
.idea/.idea.Moonlight/.idea/discord.xml
|
||||
|
|
5
.idea/.idea.Moonlight/.idea/discord.xml
generated
5
.idea/.idea.Moonlight/.idea/discord.xml
generated
|
@ -3,5 +3,10 @@
|
|||
<component name="DiscordProjectSettings">
|
||||
<option name="show" value="PROJECT_FILES" />
|
||||
<option name="description" value="" />
|
||||
<option name="theme" value="material" />
|
||||
<option name="button1Title" value="" />
|
||||
<option name="button1Url" value="" />
|
||||
<option name="button2Title" value="" />
|
||||
<option name="button2Url" value="" />
|
||||
</component>
|
||||
</project>
|
File diff suppressed because one or more lines are too long
|
@ -39,6 +39,15 @@ public class CoreConfiguration
|
|||
[Description("Specifies the location of the key .pem file to load")]
|
||||
[JsonProperty("KeyPath")]
|
||||
public string KeyPath { get; set; } = "";
|
||||
|
||||
[Description("Specifies the file upload limit per http request in megabytes")]
|
||||
[JsonProperty("UploadLimit")]
|
||||
public int UploadLimit { get; set; } = 100;
|
||||
|
||||
[Description(
|
||||
"Specifies the maximum message size moonlight can receive via the websocket connection in kilobytes")]
|
||||
[JsonProperty("MessageSizeLimit")]
|
||||
public int MessageSizeLimit { get; set; } = 1024;
|
||||
}
|
||||
|
||||
public class DatabaseData
|
||||
|
|
|
@ -3,18 +3,24 @@ using Microsoft.AspNetCore.Components;
|
|||
using MoonCore.Abstractions;
|
||||
using MoonCore.Helpers;
|
||||
using MoonCore.Services;
|
||||
using MoonCoreUI.Extensions;
|
||||
using MoonCoreUI.Services;
|
||||
using Moonlight.Core.Configuration;
|
||||
using Moonlight.Core.Database;
|
||||
using Moonlight.Core.Database.Entities;
|
||||
using Moonlight.Core.Implementations.Diagnose;
|
||||
using Moonlight.Core.Implementations.UI.Admin.AdminColumns;
|
||||
using Moonlight.Core.Implementations.UI.Index;
|
||||
using Moonlight.Core.Interfaces;
|
||||
using Moonlight.Core.Interfaces.Ui.Admin;
|
||||
using Moonlight.Core.Interfaces.UI.User;
|
||||
using Moonlight.Core.Models;
|
||||
using Moonlight.Core.Models.Abstractions;
|
||||
using Moonlight.Core.Models.Abstractions.Feature;
|
||||
using Moonlight.Core.Models.Enums;
|
||||
using Moonlight.Core.Repositories;
|
||||
using Moonlight.Core.Services;
|
||||
using Moonlight.Core.UI.Components.Cards;
|
||||
|
||||
namespace Moonlight.Core;
|
||||
|
||||
|
@ -55,24 +61,37 @@ public class CoreFeature : MoonlightFeature
|
|||
builder.Services.AddScoped<ClipboardService>();
|
||||
builder.Services.AddScoped<ModalService>();
|
||||
|
||||
// Configure interop
|
||||
ToastService.Prefix = "moonlight.toasts";
|
||||
ModalService.Prefix = "moonlight.modals";
|
||||
AlertService.Prefix = "moonlight.alerts";
|
||||
ClipboardService.Prefix = "moonlight.clipboard";
|
||||
FileDownloadService.Prefix = "moonlight.utils";
|
||||
builder.Services.AddMoonCoreUi(configuration =>
|
||||
{
|
||||
configuration.ToastJavascriptPrefix = "moonlight.toasts";
|
||||
configuration.ModalJavascriptPrefix = "moonlight.modals";
|
||||
configuration.AlertJavascriptPrefix = "moonlight.alerts";
|
||||
configuration.ClipboardJavascriptPrefix = "moonlight.clipboard";
|
||||
configuration.FileDownloadJavascriptPrefix = "moonlight.utils";
|
||||
});
|
||||
|
||||
// Add external services and blazor/asp.net stuff
|
||||
builder.Services.AddRazorPages();
|
||||
builder.Services.AddServerSideBlazor();
|
||||
builder.Services.AddHttpContextAccessor();
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddBlazorTable();
|
||||
|
||||
// Configure blazor pipeline in detail
|
||||
builder.Services.AddServerSideBlazor().AddHubOptions(options =>
|
||||
{
|
||||
options.MaximumReceiveMessageSize = ByteSizeValue.FromKiloBytes(config.Http.MessageSizeLimit).Bytes;
|
||||
});
|
||||
|
||||
// Setup authentication if required
|
||||
if (config.Authentication.UseDefaultAuthentication)
|
||||
builder.Services.AddScoped<IAuthenticationProvider, DefaultAuthenticationProvider>();
|
||||
|
||||
// Setup http upload limit
|
||||
context.Builder.WebHost.ConfigureKestrel(options =>
|
||||
{
|
||||
options.Limits.MaxRequestBodySize = ByteSizeValue.FromMegaBytes(config.Http.UploadLimit).Bytes;
|
||||
});
|
||||
|
||||
// Assets
|
||||
|
||||
// - Javascript
|
||||
|
@ -103,6 +122,12 @@ public class CoreFeature : MoonlightFeature
|
|||
// Define permissions
|
||||
var permissionService = app.Services.GetRequiredService<PermissionService>();
|
||||
|
||||
await permissionService.Register(999, new()
|
||||
{
|
||||
Name = "See Admin Page",
|
||||
Description = "Allows access to the admin page and the connected stats (server and user count)"
|
||||
});
|
||||
|
||||
await permissionService.Register(1000, new()
|
||||
{
|
||||
Name = "Manage users",
|
||||
|
@ -138,6 +163,10 @@ public class CoreFeature : MoonlightFeature
|
|||
await pluginService.RegisterImplementation<IDiagnoseAction>(new FeatureDiagnoseAction());
|
||||
await pluginService.RegisterImplementation<IDiagnoseAction>(new LogDiagnoseAction());
|
||||
|
||||
// UI
|
||||
await pluginService.RegisterImplementation<IAdminDashboardColumn>(new UserCount());
|
||||
await pluginService.RegisterImplementation<IUserDashboardComponent>(new GreetingMessages());
|
||||
|
||||
// Startup job services
|
||||
var startupJobService = app.Services.GetRequiredService<StartupJobService>();
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
using MoonCoreUI.Helpers;
|
||||
using Moonlight.Core.Interfaces.Ui.Admin;
|
||||
using Moonlight.Core.Models.Abstractions;
|
||||
using Moonlight.Core.UI.Components.Cards;
|
||||
|
||||
namespace Moonlight.Core.Implementations.UI.Admin.AdminColumns;
|
||||
|
||||
public class UserCount : IAdminDashboardColumn
|
||||
{
|
||||
public Task<UiComponent> Get()
|
||||
{
|
||||
var res = new UiComponent()
|
||||
{
|
||||
Component = ComponentHelper.FromType<AdminUserCard>(),
|
||||
Index = int.MinValue
|
||||
};
|
||||
|
||||
return Task.FromResult(res);
|
||||
}
|
||||
}
|
20
Moonlight/Core/Implementations/UI/Index/GreetingMessages.cs
Normal file
20
Moonlight/Core/Implementations/UI/Index/GreetingMessages.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using MoonCoreUI.Helpers;
|
||||
using Moonlight.Core.Interfaces.UI.User;
|
||||
using Moonlight.Core.Models.Abstractions;
|
||||
using Moonlight.Core.UI.Components.Cards;
|
||||
|
||||
namespace Moonlight.Core.Implementations.UI.Index;
|
||||
|
||||
public class GreetingMessages : IUserDashboardComponent
|
||||
{
|
||||
public Task<UiComponent> Get()
|
||||
{
|
||||
var res = new UiComponent()
|
||||
{
|
||||
Component = ComponentHelper.FromType<TimeBasedGreetingMessages>(),
|
||||
Index = int.MinValue
|
||||
};
|
||||
|
||||
return Task.FromResult(res);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
using Moonlight.Core.Models.Abstractions;
|
||||
|
||||
namespace Moonlight.Core.Interfaces.Ui.Admin;
|
||||
|
||||
public interface IAdminDashboardColumn
|
||||
{
|
||||
public Task<UiComponent> Get();
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
using Moonlight.Core.Models.Abstractions;
|
||||
|
||||
namespace Moonlight.Core.Interfaces.Ui.Admin;
|
||||
|
||||
public interface IAdminDashboardComponent
|
||||
{
|
||||
public Task<UiComponent> Get();
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
using Moonlight.Core.Models.Abstractions;
|
||||
|
||||
namespace Moonlight.Core.Interfaces.UI.User;
|
||||
|
||||
public interface IUserDashboardComponent
|
||||
{
|
||||
public Task<UiComponent> Get();
|
||||
}
|
|
@ -1,4 +1,9 @@
|
|||
using System.Reflection;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moonlight.Core.UI.Components.Partials;
|
||||
using IComponent = Microsoft.AspNetCore.Components.IComponent;
|
||||
|
||||
namespace Moonlight.Core.Models.Abstractions.Feature;
|
||||
|
||||
|
@ -6,7 +11,7 @@ public class UiInitContext
|
|||
{
|
||||
public List<SidebarItem> SidebarItems { get; set; } = new();
|
||||
public List<Assembly> RouteAssemblies { get; set; } = new();
|
||||
|
||||
|
||||
public void EnablePages<T>()
|
||||
{
|
||||
var assembly = typeof(T).Assembly;
|
||||
|
|
10
Moonlight/Core/Models/Abstractions/UiComponent.cs
Normal file
10
Moonlight/Core/Models/Abstractions/UiComponent.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Moonlight.Core.Models.Abstractions;
|
||||
|
||||
public class UiComponent
|
||||
{
|
||||
public required RenderFragment Component { get; set; }
|
||||
|
||||
public int Index { get; set; } = 0;
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
<div class="d-flex justify-content-center">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="card card-body m-15 p-15">
|
||||
<div class="card card-body m-8 m-md-15 p-8 p-md-15">
|
||||
<div class="text-center mb-8">
|
||||
<div class="fw-bold mb-3 fs-1">
|
||||
Login
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
<div class="d-flex justify-content-center">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="card card-body m-15 p-15">
|
||||
<div class="card card-body m-8 m-md-15 p-8 p-md-15">
|
||||
@if (ConfigService.Get().Authentication.DenyRegister)
|
||||
{
|
||||
<IconAlert Color="danger" Icon="bx-shield-quarter" Title="Sign up disabled">
|
||||
|
|
19
Moonlight/Core/UI/Components/Cards/AdminUserCard.razor
Normal file
19
Moonlight/Core/UI/Components/Cards/AdminUserCard.razor
Normal file
|
@ -0,0 +1,19 @@
|
|||
@using MoonCore.Abstractions
|
||||
@using Moonlight.Core.Database.Entities
|
||||
|
||||
@inject Repository<User> UserRepository
|
||||
|
||||
<a href="/admin/servers">
|
||||
<StatCard Value="@UserCount.ToString()" Description="Users" Icon="bxs-group"></StatCard>
|
||||
</a>
|
||||
|
||||
@code {
|
||||
|
||||
private int UserCount;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
UserCount = UserRepository.Get().Count();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
@using MoonCore.Services
|
||||
@using Moonlight.Core.Configuration
|
||||
@using Moonlight.Core.Services
|
||||
|
||||
@inject IdentityService IdentityService
|
||||
@inject ConfigService<CoreConfiguration> ConfigService
|
||||
|
||||
<div class="card card-body p-8">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="fs-2 fw-semibold">
|
||||
@{
|
||||
var greeting = GetGreetingMessage();
|
||||
}
|
||||
@greeting.Item1
|
||||
<span class="text-info">@IdentityService.CurrentUser.Username</span>
|
||||
@greeting.Item2
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
// For explanation:
|
||||
// The first value is the actual message
|
||||
// end second value is for question marks and so on
|
||||
//
|
||||
// and yes, this "feature" is kinda useless but still i wanted to implement
|
||||
// it. - Masu
|
||||
private (string, string) GetGreetingMessage()
|
||||
{
|
||||
var config = ConfigService.Get().Customisation;
|
||||
|
||||
if (config.DisableTimeBasedGreetingMessages)
|
||||
return ("Welcome back, ", "");
|
||||
|
||||
var time = DateTime.UtcNow.AddHours(config.GreetingTimezoneDifference);
|
||||
|
||||
if (time.Hour >= 23 || time.Hour < 5)
|
||||
return ("\ud83d\ude34 Still awake, ", "?");
|
||||
|
||||
if (time.Hour >= 5 && time.Hour < 10)
|
||||
return ("\ud83d\ude04 Good morning, ", "");
|
||||
|
||||
if (time.Hour >= 10 && time.Hour < 14)
|
||||
return ("\u2600\ufe0f Have a nice day, ", "");
|
||||
|
||||
if (time.Hour >= 14 && time.Hour < 16)
|
||||
return ("\ud83d\ude03 Good afternoon, ", "");
|
||||
|
||||
if (time.Hour >= 16 && time.Hour < 22)
|
||||
return ("\ud83c\udf25\ufe0f Have a nice evening, ", "");
|
||||
|
||||
if (time.Hour >= 22 && time.Hour < 23)
|
||||
return ("\ud83c\udf19 Sleep well, ", "");
|
||||
|
||||
return ("Welcome back ", "");
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
@inherits ErrorBoundaryBase
|
||||
|
||||
@if (Crashed || CurrentException != null)
|
||||
@if (Crashed || Exception != null)
|
||||
{
|
||||
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development" || (IdentityService.IsLoggedIn && IdentityService.CurrentUser.Permissions >= 9000))
|
||||
{
|
||||
|
|
|
@ -11,14 +11,14 @@
|
|||
else
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-3 text-center d-flex align-items-center">
|
||||
<div class="col-auto text-center d-flex align-items-center px-5">
|
||||
<i class="bx bx-lg text-white align-middle @(Icon)"></i>
|
||||
</div>
|
||||
<div class="col-9">
|
||||
<div class="col">
|
||||
<div class="fw-semibold text-primary fs-2">
|
||||
@Value
|
||||
</div>
|
||||
<div class="fs-4 mt-2">
|
||||
<div class="fs-4 mt-2 text-white">
|
||||
@Description
|
||||
</div>
|
||||
</div>
|
||||
|
|
56
Moonlight/Core/UI/Views/Admin/Index.razor
Normal file
56
Moonlight/Core/UI/Views/Admin/Index.razor
Normal file
|
@ -0,0 +1,56 @@
|
|||
@page "/admin"
|
||||
@using MoonCore.Abstractions
|
||||
@using Moonlight.Core.Database.Entities
|
||||
@using Moonlight.Core.Interfaces
|
||||
@using Moonlight.Core.Interfaces.Ui.Admin
|
||||
@using Moonlight.Core.Models.Abstractions
|
||||
@using Moonlight.Core.Services
|
||||
@using Moonlight.Features.Servers.Entities
|
||||
|
||||
@inject PluginService PluginService
|
||||
|
||||
@attribute [RequirePermission(999)]
|
||||
|
||||
<LazyLoader Load="Load">
|
||||
<div class="row mb-8">
|
||||
@foreach(var column in Columns.OrderBy(x => x.Index))
|
||||
{
|
||||
<div class="col-12 col-lg-6 col-xl">
|
||||
@column.Component
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@foreach (var component in Components.OrderBy(x => x.Index))
|
||||
{
|
||||
<div class="mb-4">
|
||||
@component.Component
|
||||
</div>
|
||||
}
|
||||
</LazyLoader>
|
||||
|
||||
|
||||
@code {
|
||||
|
||||
private List<UiComponent> Columns = new();
|
||||
private List<UiComponent> Components = new();
|
||||
|
||||
private async Task Load(LazyLoader arg)
|
||||
{
|
||||
await arg.SetText("Loading statistics...");
|
||||
|
||||
var componentImplementations = await PluginService.GetImplementations<IAdminDashboardComponent>();
|
||||
|
||||
foreach (var implementation in componentImplementations)
|
||||
{
|
||||
Components.Add(await implementation.Get());
|
||||
}
|
||||
|
||||
var columnImplementations = await PluginService.GetImplementations<IAdminDashboardColumn>();
|
||||
|
||||
foreach (var implementation in columnImplementations)
|
||||
{
|
||||
Columns.Add(await implementation.Get());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,60 +1,33 @@
|
|||
@page "/"
|
||||
|
||||
@using Moonlight.Core.Interfaces.UI.User
|
||||
@using Moonlight.Core.Models.Abstractions
|
||||
@using Moonlight.Core.Services
|
||||
@using MoonCore.Services
|
||||
@using Moonlight.Core.Configuration
|
||||
|
||||
@inject IdentityService IdentityService
|
||||
@inject ConfigService<CoreConfiguration> ConfigService
|
||||
|
||||
<div class="card card-body p-8">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="fs-2 fw-semibold">
|
||||
@{
|
||||
var greeting = GetGreetingMessage();
|
||||
}
|
||||
@greeting.Item1
|
||||
<span class="text-info">@IdentityService.CurrentUser.Username</span>
|
||||
@greeting.Item2
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@inject PluginService PluginService
|
||||
|
||||
<LazyLoader Load="Load">
|
||||
@foreach (var component in Components.OrderBy(x => x.Index))
|
||||
{
|
||||
<div class="mb-4">
|
||||
@component.Component
|
||||
</div>
|
||||
}
|
||||
</LazyLoader>
|
||||
|
||||
@code
|
||||
{
|
||||
// For explanation:
|
||||
// The first value is the actual message
|
||||
// end second value is for question marks and so on
|
||||
//
|
||||
// and yes, this "feature" is kinda useless but still i wanted to implement
|
||||
// it. - Masu
|
||||
private (string, string) GetGreetingMessage()
|
||||
private List<UiComponent> Components = new();
|
||||
|
||||
|
||||
private async Task Load(LazyLoader arg)
|
||||
{
|
||||
var config = ConfigService.Get().Customisation;
|
||||
await arg.SetText("Loading...");
|
||||
|
||||
if (config.DisableTimeBasedGreetingMessages)
|
||||
return ("Welcome back, ", "");
|
||||
var implementations = await PluginService.GetImplementations<IUserDashboardComponent>();
|
||||
|
||||
var time = DateTime.UtcNow.AddHours(config.GreetingTimezoneDifference);
|
||||
|
||||
if (time.Hour >= 23 || time.Hour < 5)
|
||||
return ("\ud83d\ude34 Still awake, ", "?");
|
||||
|
||||
if (time.Hour >= 5 && time.Hour < 10)
|
||||
return ("\ud83d\ude04 Good morning, ", "");
|
||||
|
||||
if (time.Hour >= 10 && time.Hour < 14)
|
||||
return ("\u2600\ufe0f Have a nice day, ", "");
|
||||
|
||||
if (time.Hour >= 14 && time.Hour < 16)
|
||||
return ("\ud83d\ude03 Good afternoon, ", "");
|
||||
|
||||
if (time.Hour >= 16 && time.Hour < 22)
|
||||
return ("\ud83c\udf25\ufe0f Have a nice evening, ", "");
|
||||
|
||||
if (time.Hour >= 22 && time.Hour < 23)
|
||||
return ("\ud83c\udf19 Sleep well, ", "");
|
||||
|
||||
return ("Welcome back ", "");
|
||||
foreach (var implementation in implementations)
|
||||
{
|
||||
Components.Add(await implementation.Get());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
||||
WORKDIR /app
|
||||
EXPOSE 80
|
||||
EXPOSE 443
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
|
||||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
WORKDIR /src
|
||||
COPY ["Moonlight/Moonlight.csproj", "Moonlight/"]
|
||||
|
|
|
@ -26,7 +26,6 @@ public class FileManagerFeature : MoonlightFeature
|
|||
var config = new ConfigService<CoreConfiguration>(PathBuilder.File("storage", "configs", "core.json"));
|
||||
context.Builder.Services.AddSingleton(new JwtService<FileManagerJwtType>(config.Get().Security.Token));
|
||||
|
||||
context.AddAsset("FileManager", "js/dropzone.js");
|
||||
context.AddAsset("FileManager", "js/filemanager.js");
|
||||
context.AddAsset("FileManager", "editor/ace.css");
|
||||
context.AddAsset("FileManager", "editor/ace.js");
|
||||
|
@ -55,9 +54,12 @@ public class FileManagerFeature : MoonlightFeature
|
|||
await pluginService.RegisterImplementation<IFileManagerContextAction>(new RenameContextAction());
|
||||
await pluginService.RegisterImplementation<IFileManagerContextAction>(new MoveContextAction());
|
||||
await pluginService.RegisterImplementation<IFileManagerContextAction>(new DownloadContextAction());
|
||||
await pluginService.RegisterImplementation<IFileManagerContextAction>(new ArchiveContextAction());
|
||||
await pluginService.RegisterImplementation<IFileManagerContextAction>(new ExtractContextAction());
|
||||
await pluginService.RegisterImplementation<IFileManagerContextAction>(new DeleteContextAction());
|
||||
|
||||
await pluginService.RegisterImplementation<IFileManagerSelectionAction>(new MoveSelectionAction());
|
||||
await pluginService.RegisterImplementation<IFileManagerSelectionAction>(new ArchiveSelectionAction());
|
||||
await pluginService.RegisterImplementation<IFileManagerSelectionAction>(new DeleteSelectionAction());
|
||||
|
||||
await pluginService.RegisterImplementation<IFileManagerCreateAction>(new CreateFileAction());
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
using MoonCore.Exceptions;
|
||||
using MoonCore.Helpers;
|
||||
using MoonCoreUI.Services;
|
||||
using Moonlight.Features.FileManager.Interfaces;
|
||||
using Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
|
||||
|
||||
namespace Moonlight.Features.FileManager.Implementations;
|
||||
|
||||
public class ArchiveContextAction : IFileManagerContextAction
|
||||
{
|
||||
public string Name => "Archive";
|
||||
public string Icon => "bxs-archive-in";
|
||||
public string Color => "warning";
|
||||
public Func<FileEntry, bool> Filter => _ => true;
|
||||
|
||||
public async Task Execute(BaseFileAccess access, UI.Components.FileManager fileManager, FileEntry entry,
|
||||
IServiceProvider provider)
|
||||
{
|
||||
var archiveAccess = access.Actions as IArchiveFileActions;
|
||||
|
||||
if (archiveAccess == null)
|
||||
throw new DisplayException("This file access does not support archiving");
|
||||
|
||||
var alertService = provider.GetRequiredService<AlertService>();
|
||||
|
||||
var fileName = await alertService.Text("Enter the archive file name", "",
|
||||
Formatter.FormatDate(DateTime.UtcNow) + ".tar.gz");
|
||||
|
||||
if (string.IsNullOrEmpty(fileName) || fileName.Contains("..")) // => canceled
|
||||
return;
|
||||
|
||||
var toastService = provider.GetRequiredService<ToastService>();
|
||||
|
||||
await toastService.CreateProgress("fileManagerArchive", "Archiving... Please be patient");
|
||||
|
||||
try
|
||||
{
|
||||
await archiveAccess.Archive(
|
||||
access.CurrentDirectory + fileName,
|
||||
new[] { access.CurrentDirectory + entry.Name }
|
||||
);
|
||||
|
||||
await toastService.Success("Successfully created archive");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn($"An error occured while archiving item ({entry.Name}):");
|
||||
Logger.Warn(e);
|
||||
|
||||
await toastService.Danger("An unknown error occured while creating archive");
|
||||
}
|
||||
finally
|
||||
{
|
||||
await toastService.RemoveProgress("fileManagerArchive");
|
||||
}
|
||||
|
||||
await fileManager.View.Refresh();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
using MoonCore.Exceptions;
|
||||
using MoonCore.Helpers;
|
||||
using MoonCoreUI.Services;
|
||||
using Moonlight.Features.FileManager.Interfaces;
|
||||
using Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
|
||||
|
||||
namespace Moonlight.Features.FileManager.Implementations;
|
||||
|
||||
public class ArchiveSelectionAction : IFileManagerSelectionAction
|
||||
{
|
||||
public string Name => "Archive";
|
||||
public string Color => "primary";
|
||||
|
||||
public async Task Execute(BaseFileAccess access, UI.Components.FileManager fileManager, FileEntry[] entries,
|
||||
IServiceProvider provider)
|
||||
{
|
||||
var archiveAccess = access.Actions as IArchiveFileActions;
|
||||
|
||||
if (archiveAccess == null)
|
||||
throw new DisplayException("This file access does not support archiving");
|
||||
|
||||
var alertService = provider.GetRequiredService<AlertService>();
|
||||
|
||||
var fileName = await alertService.Text("Enter the archive file name", "",
|
||||
Formatter.FormatDate(DateTime.UtcNow) + ".tar.gz");
|
||||
|
||||
if (string.IsNullOrEmpty(fileName) || fileName.Contains("..")) // => canceled
|
||||
return;
|
||||
|
||||
var toastService = provider.GetRequiredService<ToastService>();
|
||||
|
||||
await toastService.CreateProgress("fileManagerArchive", "Archiving... Please be patient");
|
||||
|
||||
try
|
||||
{
|
||||
await archiveAccess.Archive(
|
||||
access.CurrentDirectory + fileName,
|
||||
entries.Select(x => access.CurrentDirectory + x.Name).ToArray()
|
||||
);
|
||||
|
||||
await toastService.Success("Successfully created archive");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn($"An error occured while archiving items ({entries.Length}):");
|
||||
Logger.Warn(e);
|
||||
|
||||
await toastService.Danger("An unknown error occured while creating archive");
|
||||
}
|
||||
finally
|
||||
{
|
||||
await toastService.RemoveProgress("fileManagerArchive");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
using MoonCore.Exceptions;
|
||||
using MoonCore.Helpers;
|
||||
using MoonCoreUI.Services;
|
||||
using Moonlight.Features.FileManager.Interfaces;
|
||||
using Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
|
||||
|
||||
namespace Moonlight.Features.FileManager.Implementations;
|
||||
|
||||
public class ExtractContextAction : IFileManagerContextAction
|
||||
{
|
||||
public string Name => "Extract";
|
||||
public string Icon => "bxs-archive-out";
|
||||
public string Color => "warning";
|
||||
public Func<FileEntry, bool> Filter => entry => entry.IsFile && entry.Name.EndsWith(".tar.gz");
|
||||
public async Task Execute(BaseFileAccess access, UI.Components.FileManager fileManager, FileEntry entry, IServiceProvider provider)
|
||||
{
|
||||
var archiveAccess = access.Actions as IArchiveFileActions;
|
||||
|
||||
if (archiveAccess == null)
|
||||
throw new DisplayException("This file access does not support archiving");
|
||||
|
||||
await fileManager.OpenFolderSelect("Select where you want to extract the content of the archive", async destination =>
|
||||
{
|
||||
var toastService = provider.GetRequiredService<ToastService>();
|
||||
|
||||
await toastService.CreateProgress("fileManagerExtract", "Extracting... Please be patient");
|
||||
|
||||
try
|
||||
{
|
||||
await archiveAccess.Extract(
|
||||
access.CurrentDirectory + entry.Name,
|
||||
destination
|
||||
);
|
||||
|
||||
await toastService.Success("Successfully extracted archive");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn($"An error occured while extracting archive ({entry.Name}):");
|
||||
Logger.Warn(e);
|
||||
|
||||
await toastService.Danger("An unknown error occured while extracting archive");
|
||||
}
|
||||
finally
|
||||
{
|
||||
await toastService.RemoveProgress("fileManagerExtract");
|
||||
}
|
||||
|
||||
await fileManager.View.Refresh();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -4,9 +4,9 @@ namespace Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
|
|||
|
||||
public class BaseFileAccess : IDisposable
|
||||
{
|
||||
private readonly IFileActions Actions;
|
||||
public readonly IFileActions Actions;
|
||||
|
||||
private string CurrentDirectory = "/";
|
||||
public string CurrentDirectory { get; private set; } = "/";
|
||||
|
||||
public BaseFileAccess(IFileActions actions)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
namespace Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
|
||||
|
||||
public interface IArchiveFileActions
|
||||
{
|
||||
public Task Archive(string path, string[] files);
|
||||
public Task Extract(string path, string destination);
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
namespace Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
|
||||
|
||||
public interface IFileCompressAccess
|
||||
{
|
||||
public Task Compress(string[] names);
|
||||
public Task Decompress(string name);
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
namespace Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
|
||||
|
||||
public interface IFileLaunchAccess
|
||||
{
|
||||
public Task<string> GetLaunchUrl();
|
||||
}
|
|
@ -30,7 +30,11 @@ public class FileManagerInteropService
|
|||
|
||||
public async Task UpdateUrl(string urlId, string url)
|
||||
{
|
||||
await JsRuntime.InvokeVoidAsync("filemanager.updateUrl", urlId, url);
|
||||
try
|
||||
{
|
||||
await JsRuntime.InvokeVoidAsync("filemanager.updateUrl", urlId, url);
|
||||
}
|
||||
catch (TaskCanceledException) { /* ignored */ }
|
||||
}
|
||||
|
||||
[JSInvokable]
|
||||
|
|
|
@ -95,7 +95,8 @@ else
|
|||
OnEntryClicked="OnEntryClicked"
|
||||
OnNavigateUpClicked="OnNavigateUpClicked"
|
||||
OnSelectionChanged="OnSelectionChanged"
|
||||
EnableContextMenu="true">
|
||||
EnableContextMenu="true"
|
||||
ShowUploadPrompt="true">
|
||||
<ContextMenuTemplate>
|
||||
@foreach (var action in ContextActions)
|
||||
{
|
||||
|
|
|
@ -209,6 +209,15 @@
|
|||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@if (Entries.Length == 0 && ShowUploadPrompt)
|
||||
{
|
||||
<div class="py-4">
|
||||
<IconAlert Color="primary" Title="No files and folders found" Icon="bx-cloud-upload">
|
||||
Drag and drop files and folders here to start uploading them or click on the upload button on the top
|
||||
</IconAlert>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (EnableContextMenu && ContextMenuTemplate != null)
|
||||
|
@ -231,6 +240,7 @@
|
|||
[Parameter] public bool ShowDate { get; set; } = true;
|
||||
[Parameter] public bool ShowSelect { get; set; } = true;
|
||||
[Parameter] public bool ShowNavigateUp { get; set; } = true;
|
||||
[Parameter] public bool ShowUploadPrompt { get; set; } = false;
|
||||
|
||||
[Parameter] public RenderFragment<FileEntry>? ContextMenuTemplate { get; set; }
|
||||
[Parameter] public bool EnableContextMenu { get; set; } = false;
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
using System.ComponentModel;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Moonlight.Features.Servers.Configuration;
|
||||
|
||||
public class ServersConfiguration
|
||||
{
|
||||
|
||||
[JsonProperty("DisableServerKillWarning")]
|
||||
[Description("With this option you can globally disable the confirmation popup shown when killing a server")]
|
||||
public bool DisableServerKillWarning { get; set; } = false;
|
||||
}
|
|
@ -9,6 +9,7 @@ using Moonlight.Features.Servers.Entities;
|
|||
using Moonlight.Features.Servers.Models;
|
||||
using Moonlight.Features.Servers.Models.Json;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Moonlight.Features.Servers.Helpers;
|
||||
|
||||
|
@ -30,7 +31,7 @@ public class ImageConversionHelper
|
|||
.Include(x => x.Variables)
|
||||
.First(x => x.Id == image.Id);
|
||||
|
||||
|
||||
|
||||
var model = new ImageJson()
|
||||
{
|
||||
Name = imageWithData.Name,
|
||||
|
@ -54,7 +55,7 @@ public class ImageConversionHelper
|
|||
|
||||
foreach (var variable in imageWithData.Variables)
|
||||
model.Variables.Add(Mapper.Map<ImageJson.ImageVariable>(variable));
|
||||
|
||||
|
||||
foreach (var dockerImage in imageWithData.DockerImages)
|
||||
model.DockerImages.Add(Mapper.Map<ImageJson.ImageDockerImage>(dockerImage));
|
||||
|
||||
|
@ -88,20 +89,21 @@ public class ImageConversionHelper
|
|||
|
||||
foreach (var variable in model.Variables)
|
||||
result.Variables.Add(Mapper.Map<ServerImageVariable>(variable));
|
||||
|
||||
|
||||
foreach (var dockerImage in model.DockerImages)
|
||||
result.DockerImages.Add(Mapper.Map<ServerDockerImage>(dockerImage));
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
public Task<ServerImage> ImportFromEggJson(string json)
|
||||
|
||||
// Old import function which used the microsoft json parsing
|
||||
public Task<ServerImage> ImportFromEggJson_Old(string json)
|
||||
{
|
||||
var fixedJson = json;
|
||||
|
||||
fixedJson = fixedJson.Replace("\\/", "/");
|
||||
|
||||
// Note: We use microsofts config system here as its dynamic and probably the best parsing method to use here
|
||||
|
||||
// Note: We use microsofts config system instead of newtonsoft.json as its dynamic and probably the best parsing method to use here
|
||||
var eggData = new ConfigurationBuilder()
|
||||
.AddJsonString(fixedJson)
|
||||
.Build();
|
||||
|
@ -114,7 +116,11 @@ public class ImageConversionHelper
|
|||
result.Author = eggData["author"] ?? "Author was missing";
|
||||
result.StartupCommand = eggData["startup"] ?? "Startup was missing";
|
||||
result.StopCommand = eggData.GetSection("config")["stop"] ?? "Stop command was missing";
|
||||
|
||||
|
||||
// Some weird eggs use ^^C in as a stop command, so we need to handle this as well
|
||||
// because moonlight handles power signals correctly, wings does/did not
|
||||
result.StopCommand = result.StopCommand.Replace("^^C", "^C");
|
||||
|
||||
// Startup detection
|
||||
var startupDetectionData = new ConfigurationBuilder()
|
||||
.AddJsonString(eggData.GetSection("config")["startup"] ?? "{}")
|
||||
|
@ -122,14 +128,14 @@ public class ImageConversionHelper
|
|||
|
||||
// Node Regex: As the online detection uses regex, we want to escape any special chars from egg imports
|
||||
// as eggs dont use regex and as such may contain characters which regex uses as meta characters.
|
||||
// Without this escaping, many startup detection string wont work
|
||||
// Without this escaping, many startup detection strings wont work
|
||||
result.OnlineDetection = Regex.Escape(startupDetectionData["done"] ?? "Online detection was missing");
|
||||
|
||||
// Docker image method 1:
|
||||
// Reference egg: https://github.com/parkervcp/eggs/blob/master/game_eggs/mindustry/egg-mindustry.json
|
||||
if (eggData["image"] != null)
|
||||
{
|
||||
result.DockerImages.Add(new ()
|
||||
result.DockerImages.Add(new()
|
||||
{
|
||||
Name = eggData["image"]!,
|
||||
AutoPull = true,
|
||||
|
@ -143,14 +149,14 @@ public class ImageConversionHelper
|
|||
|
||||
foreach (var dockerImage in dockerImagesSection.GetChildren())
|
||||
{
|
||||
result.DockerImages.Add(new ()
|
||||
result.DockerImages.Add(new()
|
||||
{
|
||||
Name = dockerImage.Value ?? "Docker image name was missing",
|
||||
AutoPull = true,
|
||||
DisplayName = dockerImage.Key
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Docker image method 3:
|
||||
// Reference egg: https://github.com/parkervcp/eggs/blob/master/game_eggs/minecraft/java/cuberite/egg-cuberite.json
|
||||
var dockerImagesList = eggData.GetValue<List<string>>("images");
|
||||
|
@ -159,7 +165,7 @@ public class ImageConversionHelper
|
|||
{
|
||||
foreach (var imageName in dockerImagesList)
|
||||
{
|
||||
result.DockerImages.Add(new ()
|
||||
result.DockerImages.Add(new()
|
||||
{
|
||||
Name = imageName,
|
||||
AutoPull = true,
|
||||
|
@ -167,14 +173,14 @@ public class ImageConversionHelper
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Parse config
|
||||
var parseConfigData = new ConfigurationBuilder()
|
||||
.AddJsonString(eggData.GetSection("config")["files"] ?? "{}")
|
||||
.Build();
|
||||
|
||||
var parseConfigModels = new List<ServerParseConfig>();
|
||||
|
||||
|
||||
foreach (var fileSection in parseConfigData.GetChildren())
|
||||
{
|
||||
var model = new ServerParseConfig()
|
||||
|
@ -189,20 +195,23 @@ public class ImageConversionHelper
|
|||
|
||||
valueWithChecks = valueWithChecks.Replace("server.build.default.port", "SERVER_PORT");
|
||||
valueWithChecks = valueWithChecks.Replace("server.build.env.", "");
|
||||
|
||||
|
||||
model.Configuration.Add(findSection.Key, valueWithChecks);
|
||||
}
|
||||
|
||||
|
||||
parseConfigModels.Add(model);
|
||||
}
|
||||
|
||||
result.ParseConfiguration = JsonConvert.SerializeObject(parseConfigModels);
|
||||
|
||||
|
||||
// Installation
|
||||
result.InstallShell = "/bin/" + (eggData.GetSection("scripts").GetSection("installation")["entrypoint"] ?? "Install shell was missing");
|
||||
result.InstallScript = eggData.GetSection("scripts").GetSection("installation")["script"] ?? "Install script was missing";
|
||||
result.InstallDockerImage = eggData.GetSection("scripts").GetSection("installation")["container"] ?? "Install script was missing";
|
||||
|
||||
result.InstallShell = "/bin/" + (eggData.GetSection("scripts").GetSection("installation")["entrypoint"] ??
|
||||
"Install shell was missing");
|
||||
result.InstallScript = eggData.GetSection("scripts").GetSection("installation")["script"] ??
|
||||
"Install script was missing";
|
||||
result.InstallDockerImage = eggData.GetSection("scripts").GetSection("installation")["container"] ??
|
||||
"Install script was missing";
|
||||
|
||||
// Variables
|
||||
foreach (var variableSection in eggData.GetSection("variables").GetChildren())
|
||||
{
|
||||
|
@ -225,10 +234,159 @@ public class ImageConversionHelper
|
|||
variable.AllowView = variableSection.GetValue<int>("user_viewable") == 1;
|
||||
variable.AllowEdit = variableSection.GetValue<int>("user_editable") == 1;
|
||||
}
|
||||
|
||||
|
||||
result.Variables.Add(variable);
|
||||
}
|
||||
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
public Task<ServerImage> ImportFromEggJson(string json)
|
||||
{
|
||||
// Prepare json
|
||||
var fixedJson = json;
|
||||
fixedJson = fixedJson.Replace("\\/", "/");
|
||||
|
||||
// Prepare result object and set moonlight native fields
|
||||
var result = new ServerImage();
|
||||
|
||||
result.AllocationsNeeded = 1;
|
||||
result.AllowDockerImageChange = true;
|
||||
|
||||
//
|
||||
var egg = JObject.Parse(fixedJson);
|
||||
|
||||
result.AllocationsNeeded = 1; // We cannot convert this value as its moonlight native
|
||||
|
||||
// Basic values
|
||||
result.Name = egg["name"]?.Value<string>() ?? "Name was missing";
|
||||
result.Author = egg["author"]?.Value<string>() ?? "Author was missing";
|
||||
result.StartupCommand = egg["startup"]?.Value<string>() ?? "Startup was missing";
|
||||
result.StopCommand = egg["config"]?["stop"]?.Value<string>() ?? "Stop command was missing";
|
||||
|
||||
// Some weird eggs use ^^C in as a stop command, so we need to handle this as well
|
||||
// because moonlight handles power signals correctly, wings does/did not
|
||||
result.StopCommand = result.StopCommand.Replace("^^C", "^C");
|
||||
|
||||
// Startup detection
|
||||
var startup = JObject.Parse(egg["config"]?["startup"]?.Value<string>() ?? "{}");
|
||||
|
||||
// Node Regex: As the online detection uses regex, we want to escape any special chars from egg imports
|
||||
// as eggs dont use regex and as such may contain characters which regex uses as meta characters.
|
||||
// Without this escaping, many startup detection strings wont work
|
||||
result.OnlineDetection = Regex.Escape(startup["done"]?.Value<string>() ?? "Online detection was missing");
|
||||
|
||||
// Docker images
|
||||
|
||||
// Docker image method 1:
|
||||
// Reference egg: https://github.com/parkervcp/eggs/blob/master/game_eggs/mindustry/egg-mindustry.json
|
||||
if (egg["image"] != null)
|
||||
{
|
||||
result.DockerImages.Add(new()
|
||||
{
|
||||
Name = egg["image"]?.Value<string>() ?? "Docker image not specified",
|
||||
AutoPull = true,
|
||||
DisplayName = egg["image"]?.Value<string>() ?? "Docker image not specified"
|
||||
});
|
||||
}
|
||||
|
||||
// Docker image method 2:
|
||||
// Reference egg: https://github.com/parkervcp/eggs/blob/master/game_eggs/minecraft/java/cuberite/egg-cuberite.json
|
||||
if (egg["images"] != null)
|
||||
{
|
||||
var images = egg["images"]?.ToObject<JArray>() ?? JArray.Parse("[]");
|
||||
|
||||
foreach (var imageName in images)
|
||||
{
|
||||
result.DockerImages.Add(new()
|
||||
{
|
||||
Name = imageName.Value<string>() ?? "Docker image name not found",
|
||||
AutoPull = true,
|
||||
DisplayName = imageName.Value<string>() ?? "Docker image name not found",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Docker image method 3:
|
||||
// Reference egg: https://github.com/parkervcp/eggs/blob/master/game_eggs/minecraft/java/paper/egg-paper.json
|
||||
if (egg["docker_images"] != null)
|
||||
{
|
||||
var images = egg["docker_images"]?.ToObject<JObject>() ?? JObject.Parse("{}");
|
||||
|
||||
foreach (var kvp in images)
|
||||
{
|
||||
result.DockerImages.Add(new()
|
||||
{
|
||||
Name = kvp.Value?.Value<string>() ?? kvp.Key,
|
||||
AutoPull = true,
|
||||
DisplayName = kvp.Key
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Parse config
|
||||
var parseConfig = JObject.Parse(egg["config"]?["files"]?.Value<string>() ?? "{}");
|
||||
var parseConfigModels = new List<ServerParseConfig>();
|
||||
|
||||
foreach (var config in parseConfig)
|
||||
{
|
||||
var model = new ServerParseConfig()
|
||||
{
|
||||
File = config.Key,
|
||||
Type = config.Value?["parser"]?.Value<string>() ?? "parser was missing"
|
||||
};
|
||||
|
||||
if (config.Value?["find"] == null)
|
||||
continue;
|
||||
|
||||
foreach (var findSection in config.Value!["find"]!.ToObject<JObject>() ?? JObject.Parse("{}"))
|
||||
{
|
||||
var valueWithChecks = findSection.Value?.Value<string>() ?? "Find value was null";
|
||||
|
||||
valueWithChecks = valueWithChecks.Replace("server.build.default.port", "SERVER_PORT");
|
||||
valueWithChecks = valueWithChecks.Replace("server.build.env.", "");
|
||||
|
||||
model.Configuration.Add(findSection.Key, valueWithChecks);
|
||||
}
|
||||
|
||||
parseConfigModels.Add(model);
|
||||
}
|
||||
|
||||
result.ParseConfiguration = JsonConvert.SerializeObject(parseConfigModels);
|
||||
|
||||
// Installation
|
||||
var installation = egg["scripts"]?["installation"] ?? JObject.Parse("{}");
|
||||
|
||||
result.InstallShell = "/bin/" + installation.Value<string>("entrypoint") ?? "Install shell was missing";
|
||||
result.InstallScript = installation.Value<string>("script") ?? "Install script was missing";
|
||||
result.InstallDockerImage = installation.Value<string>("container") ?? "Install container was missing";
|
||||
|
||||
// Variables
|
||||
foreach (var variableSection in egg["variables"]?.Children() ?? JEnumerable<JToken>.Empty)
|
||||
{
|
||||
var variable = new ServerImageVariable()
|
||||
{
|
||||
DisplayName = variableSection.Value<string>("name") ?? "Name was missing",
|
||||
Description = variableSection.Value<string>("description") ?? "Description was missing",
|
||||
Key = variableSection.Value<string>("env_variable") ?? "Environment variable was missing",
|
||||
DefaultValue = variableSection.Value<string>("default_value") ?? "Default value was missing",
|
||||
};
|
||||
|
||||
// Check if it is a bool value
|
||||
if (bool.TryParse(variableSection["user_viewable"]?.Value<string>(), out _))
|
||||
{
|
||||
variable.AllowView = variableSection.Value<bool>("user_viewable");
|
||||
variable.AllowEdit = variableSection.Value<bool>("user_editable");
|
||||
}
|
||||
else
|
||||
{
|
||||
variable.AllowView = variableSection.Value<int>("user_viewable") == 1;
|
||||
variable.AllowEdit = variableSection.Value<int>("user_editable") == 1;
|
||||
}
|
||||
|
||||
result.Variables.Add(variable);
|
||||
}
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ using Moonlight.Features.Servers.Exceptions;
|
|||
|
||||
namespace Moonlight.Features.Servers.Helpers;
|
||||
|
||||
public class ServerApiFileActions : IFileActions
|
||||
public class ServerApiFileActions : IFileActions, IArchiveFileActions
|
||||
{
|
||||
private readonly string Endpoint;
|
||||
private readonly string Token;
|
||||
|
@ -43,6 +43,16 @@ public class ServerApiFileActions : IFileActions
|
|||
public async Task WriteFileStream(string path, Stream dataStream) =>
|
||||
await ApiClient.PostFile($"writeFileStream?path={path}", dataStream, "upload");
|
||||
|
||||
public async Task Archive(string path, string[] files)
|
||||
{
|
||||
await ApiClient.Post($"archive?path={path}&provider=tar.gz", files);
|
||||
}
|
||||
|
||||
public async Task Extract(string path, string destination)
|
||||
{
|
||||
await ApiClient.Post($"extract?path={path}&destination={destination}&provider=tar.gz");
|
||||
}
|
||||
|
||||
public IFileActions Clone() => new ServerApiFileActions(Endpoint, Token, ServerId);
|
||||
|
||||
public void Dispose() => ApiClient.Dispose();
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
using MoonCoreUI.Helpers;
|
||||
using Moonlight.Core.Interfaces.Ui.Admin;
|
||||
using Moonlight.Core.Models.Abstractions;
|
||||
using Moonlight.Features.Servers.UI.Components.Cards;
|
||||
|
||||
namespace Moonlight.Features.Servers.Implementations.UI.Admin.AdminColumns;
|
||||
|
||||
public class ServerCount : IAdminDashboardColumn
|
||||
{
|
||||
public Task<UiComponent> Get()
|
||||
{
|
||||
var res = new UiComponent()
|
||||
{
|
||||
Component = ComponentHelper.FromType<AdminServersCard>()
|
||||
};
|
||||
|
||||
return Task.FromResult(res);
|
||||
}
|
||||
}
|
|
@ -2,13 +2,17 @@ using MoonCore.Helpers;
|
|||
using MoonCore.Services;
|
||||
using Moonlight.Core.Configuration;
|
||||
using Moonlight.Core.Interfaces;
|
||||
using Moonlight.Core.Interfaces.Ui.Admin;
|
||||
using Moonlight.Core.Models.Abstractions.Feature;
|
||||
using Moonlight.Core.Services;
|
||||
using Moonlight.Features.Servers.Actions;
|
||||
using Moonlight.Features.Servers.Configuration;
|
||||
using Moonlight.Features.Servers.Http.Middleware;
|
||||
using Moonlight.Features.Servers.Implementations.Diagnose;
|
||||
using Moonlight.Features.Servers.Implementations.UI.Admin.AdminColumns;
|
||||
using Moonlight.Features.Servers.Models.Enums;
|
||||
using Moonlight.Features.Servers.Services;
|
||||
using Moonlight.Features.Servers.UI.Components.Cards;
|
||||
|
||||
namespace Moonlight.Features.Servers;
|
||||
|
||||
|
@ -29,6 +33,11 @@ public class ServersFeature : MoonlightFeature
|
|||
var config = new ConfigService<CoreConfiguration>(PathBuilder.File("storage", "configs", "core.json"));
|
||||
context.Builder.Services.AddSingleton(new JwtService<ServersJwtType>(config.Get().Security.Token));
|
||||
|
||||
//
|
||||
var configService = new ConfigService<ServersConfiguration>(PathBuilder.File("storage", "configs", "servers.json"));
|
||||
context.Builder.Services.AddSingleton(configService);
|
||||
|
||||
// Assets
|
||||
context.AddAsset("Servers", "css/XtermBlazor.css");
|
||||
|
||||
context.AddAsset("Servers", "css/apexcharts.css");
|
||||
|
@ -89,6 +98,8 @@ public class ServersFeature : MoonlightFeature
|
|||
var pluginService = app.Services.GetRequiredService<PluginService>();
|
||||
|
||||
await pluginService.RegisterImplementation<IDiagnoseAction>(new NodesDiagnoseAction());
|
||||
|
||||
await pluginService.RegisterImplementation<IAdminDashboardColumn>(new ServerCount());
|
||||
}
|
||||
|
||||
public override Task OnUiInitialized(UiInitContext context)
|
||||
|
|
|
@ -2,6 +2,7 @@ using Microsoft.EntityFrameworkCore;
|
|||
using MoonCore.Abstractions;
|
||||
using MoonCore.Attributes;
|
||||
using MoonCore.Exceptions;
|
||||
using MoonCore.Helpers;
|
||||
using MoonCore.Services;
|
||||
using Moonlight.Core.Configuration;
|
||||
using Moonlight.Core.Database.Entities;
|
||||
|
@ -22,6 +23,8 @@ public class ServerService
|
|||
public ServerConsoleService Console => ServiceProvider.GetRequiredService<ServerConsoleService>();
|
||||
public ServerBackupService Backup => ServiceProvider.GetRequiredService<ServerBackupService>();
|
||||
public ServerScheduleService Schedule => ServiceProvider.GetRequiredService<ServerScheduleService>();
|
||||
|
||||
public NodeService NodeService => ServiceProvider.GetRequiredService<NodeService>();
|
||||
|
||||
private readonly IServiceProvider ServiceProvider;
|
||||
|
||||
|
@ -74,6 +77,19 @@ public class ServerService
|
|||
// Load node
|
||||
var node = nodeRepo.Get().First(x => x.Id == form.Node.Id);
|
||||
|
||||
// Check if node is available
|
||||
try
|
||||
{
|
||||
await NodeService.GetStatus(node);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn($"Could not establish to the node with the id {node.Id}");
|
||||
Logger.Warn(e);
|
||||
|
||||
throw new DisplayException($"Could not establish connection to the node: {e.Message}");
|
||||
}
|
||||
|
||||
// Load user
|
||||
var user = userRepo.Get().First(x => x.Id == form.Owner.Id);
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
@using MoonCore.Abstractions
|
||||
@using Moonlight.Features.Servers.Entities
|
||||
|
||||
@inject Repository<Server> ServerRepository
|
||||
|
||||
<a href="/admin/servers">
|
||||
<StatCard Value="@ServerCount.ToString()" Description="Servers" Icon="bxs-server"></StatCard>
|
||||
</a>
|
||||
|
||||
@code {
|
||||
|
||||
private int ServerCount;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
ServerCount = ServerRepository.Get().Count();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
@using Moonlight.Features.Servers.Entities
|
||||
@using Moonlight.Features.Servers.Models.Abstractions
|
||||
@using Moonlight.Features.Servers.Models.Enums
|
||||
@using Moonlight.Features.Servers.Services
|
||||
@using Moonlight.Features.Servers.UI.Components
|
||||
|
@ -11,102 +10,116 @@
|
|||
@using Moonlight.Features.Servers.UI.UserViews
|
||||
@using System.Net.Sockets
|
||||
@using System.Net.WebSockets
|
||||
@using MoonCore.Exceptions
|
||||
@using Moonlight.Features.Servers.Configuration
|
||||
@using MoonCore.Services
|
||||
@using Moonlight.Core.Services
|
||||
|
||||
@inject Repository<Server> ServerRepository
|
||||
@inject ServerService ServerService
|
||||
@inject ToastService ToastService
|
||||
@inject AlertService AlertService
|
||||
@inject IdentityService IdentityService
|
||||
@inject ConfigService<ServersConfiguration> ConfigService
|
||||
|
||||
@implements IDisposable
|
||||
|
||||
<LazyLoader Load="Load" ShowAsCard="true">
|
||||
<div class="card card-body pb-5 pt-5">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="d-flex flex-row">
|
||||
<div class="d-flex flex-column ms-3">
|
||||
<span class="fw-bold text-gray-900 fs-3">
|
||||
<div>
|
||||
<div class="row px-2">
|
||||
<div class="col-12 col-sm-3">
|
||||
<span class="fw-bold text-gray-900 fs-3 d-block">
|
||||
@Server.Name
|
||||
</span>
|
||||
<span class="text-gray-500 pt-2 fw-semibold fs-6">
|
||||
<span class="text-gray-500 pt-2 fw-semibold fs-6 d-block">
|
||||
@(Server.Image.Name)
|
||||
</span>
|
||||
</div>
|
||||
<div class="vr mx-4"></div>
|
||||
<div class="d-flex flex-column">
|
||||
<div class="text-gray-900 fs-4">
|
||||
@{
|
||||
var color = ServerUtilsHelper.GetColorFromState(Console.State);
|
||||
}
|
||||
<div class="vr p-0 mx-4 d-none d-sm-block"></div>
|
||||
<hr class="col-sm my-4 d-block d-sm-none"/>
|
||||
<div class="col-12 col-sm">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="text-gray-900 fs-4">
|
||||
@{
|
||||
var color = ServerUtilsHelper.GetColorFromState(Console.State);
|
||||
}
|
||||
|
||||
<i class="bx bx-sm bxs-circle text-@(color) @(Console.State != ServerState.Offline ? $"pulse pulse-{color}" : "") align-middle"></i>
|
||||
<span class="align-middle">
|
||||
@(Console.State)
|
||||
<span class="text-muted">(@(Formatter.FormatUptime(DateTime.UtcNow - Console.LastStateChangeTimestamp)))</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-gray-800 pt-3 fw-semibold fs-5 row">
|
||||
<div class="col-auto">
|
||||
<span>
|
||||
<i class="bx bx-sm bx-globe align-middle text-info"></i>
|
||||
<span class="align-middle">@(Server.Node.Fqdn):@(Server.MainAllocation.Port)</span>
|
||||
<i class="bx bx-sm bxs-circle text-@(color) @(Console.State != ServerState.Offline ? $"pulse pulse-{color}" : "") align-middle"></i>
|
||||
<span class="align-middle">
|
||||
@(Console.State)
|
||||
<span class="text-muted">(@(Formatter.FormatUptime(DateTime.UtcNow - Console.LastStateChangeTimestamp)))</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-gray-800 pt-3 fw-semibold fs-5 row">
|
||||
<div class="col-auto">
|
||||
<span>
|
||||
<i class="bx bx-sm bx-globe align-middle text-info"></i>
|
||||
<span class="align-middle">@(Server.Node.Fqdn):@(Server.MainAllocation.Port)</span>
|
||||
</span>
|
||||
</div>
|
||||
@*
|
||||
<div class="col-auto">
|
||||
<span>
|
||||
<i class="bx bx-sm bx-globe align-middle text-info"></i>
|
||||
<span class="align-middle">188.75.252.37:10324</span>
|
||||
</span>
|
||||
</div>
|
||||
*@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<span>
|
||||
<i class="bx bx-sm bx-globe align-middle text-info"></i>
|
||||
<span class="align-middle">188.75.252.37:10324</span>
|
||||
</span>
|
||||
<div class="mt-2">
|
||||
@if (Console.State == ServerState.Offline)
|
||||
{
|
||||
<WButton
|
||||
OnClick="Start"
|
||||
CssClasses="btn btn-light-success btn-icon me-1 my-1">
|
||||
<i class="bx bx-sm bx-play"></i>
|
||||
</WButton>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn btn-light-success btn-icon me-1 my-1 disabled" disabled="">
|
||||
<i class="bx bx-sm bx-play"></i>
|
||||
</button>
|
||||
}
|
||||
|
||||
@if (Console.State == ServerState.Offline || Console.State == ServerState.Installing)
|
||||
{
|
||||
<button class="btn btn-light-warning btn-icon me-1 my-1 disabled" disabled="">
|
||||
<i class="bx bx-sm bx-power-off"></i>
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<WButton
|
||||
OnClick="Stop"
|
||||
CssClasses="btn btn-light-warning btn-icon me-1 my-1">
|
||||
<i class="bx bx-sm bx-power-off"></i>
|
||||
</WButton>
|
||||
}
|
||||
|
||||
@if (Console.State == ServerState.Offline || Console.State == ServerState.Installing)
|
||||
{
|
||||
<button class="btn btn-light-danger btn-icon me-1 my-1 disabled" disabled="">
|
||||
<i class="bx bx-sm bx-bomb"></i>
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<WButton
|
||||
OnClick="Kill"
|
||||
CssClasses="btn btn-light-danger btn-icon me-1 my-1">
|
||||
<i class="bx bx-sm bx-bomb"></i>
|
||||
</WButton>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mt-2">
|
||||
@if (Console.State == ServerState.Offline)
|
||||
{
|
||||
<WButton
|
||||
OnClick="Start"
|
||||
CssClasses="btn btn-light-success btn-icon me-1 my-1">
|
||||
<i class="bx bx-sm bx-play"></i>
|
||||
</WButton>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn btn-light-success btn-icon me-1 my-1 disabled" disabled="">
|
||||
<i class="bx bx-sm bx-play"></i>
|
||||
</button>
|
||||
}
|
||||
|
||||
@if (Console.State == ServerState.Offline || Console.State == ServerState.Installing)
|
||||
{
|
||||
<button class="btn btn-light-warning btn-icon me-1 my-1 disabled" disabled="">
|
||||
<i class="bx bx-sm bx-power-off"></i>
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<WButton
|
||||
OnClick="Stop"
|
||||
CssClasses="btn btn-light-warning btn-icon me-1 my-1">
|
||||
<i class="bx bx-sm bx-power-off"></i>
|
||||
</WButton>
|
||||
}
|
||||
|
||||
@if (Console.State == ServerState.Offline || Console.State == ServerState.Installing)
|
||||
{
|
||||
<button class="btn btn-light-danger btn-icon me-1 my-1 disabled" disabled="">
|
||||
<i class="bx bx-sm bx-bomb"></i>
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<WButton
|
||||
OnClick="Kill"
|
||||
CssClasses="btn btn-light-danger btn-icon me-1 my-1">
|
||||
<i class="bx bx-sm bx-bomb"></i>
|
||||
</WButton>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -211,6 +224,12 @@
|
|||
.Include(x => x.Owner)
|
||||
.First(x => x.Id == Id);
|
||||
|
||||
if (Server.Owner.Id != IdentityService.CurrentUser.Id && IdentityService.CurrentUser.Permissions < 5000)
|
||||
{
|
||||
Server = null!;
|
||||
return;
|
||||
}
|
||||
|
||||
await lazyLoader.SetText("Establishing a connection to the server");
|
||||
|
||||
// Create console wrapper
|
||||
|
@ -301,11 +320,39 @@
|
|||
await InstallTerminal.WriteLine(message);
|
||||
}
|
||||
|
||||
private async Task Start() => await ServerService.Console.SendAction(Server, PowerAction.Start);
|
||||
private async Task Start() => await SendSignalHandled(PowerAction.Start);
|
||||
|
||||
private async Task Stop() => await ServerService.Console.SendAction(Server, PowerAction.Stop);
|
||||
private async Task Stop() => await SendSignalHandled(PowerAction.Stop);
|
||||
|
||||
private async Task Kill() => await ServerService.Console.SendAction(Server, PowerAction.Kill);
|
||||
private async Task Kill()
|
||||
{
|
||||
if (!ConfigService.Get().DisableServerKillWarning)
|
||||
{
|
||||
if (!await AlertService.YesNo("Do you really want to kill the server? This can result in data loss or corrupted server files"))
|
||||
return;
|
||||
}
|
||||
|
||||
await SendSignalHandled(PowerAction.Kill);
|
||||
}
|
||||
|
||||
private async Task SendSignalHandled(PowerAction action)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ServerService.Console.SendAction(Server, action);
|
||||
}
|
||||
catch (DisplayException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn($"An error occured while sending power action {action} to server {Server.Id}:");
|
||||
Logger.Warn(e);
|
||||
|
||||
await ToastService.Danger("An error occured while sending power action to server. Check the console for more information");
|
||||
}
|
||||
}
|
||||
|
||||
public async void Dispose()
|
||||
{
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
</div>
|
||||
<div class="col-md-3 col-12">
|
||||
@{
|
||||
var memoryText = $"{Formatter.FormatSize(Status.Hardware.Memory.Total - Status.Hardware.Memory.Available - Status.Hardware.Memory.Free)} / {Formatter.FormatSize(Status.Hardware.Memory.Total)}";
|
||||
var memoryText = $"{Formatter.FormatSize(Status.Hardware.Memory.Total - (Status.Hardware.Memory.Available + Status.Hardware.Memory.Cached))} / {Formatter.FormatSize(Status.Hardware.Memory.Total)}";
|
||||
}
|
||||
|
||||
<StatCard Value="@memoryText" Description="Memory usage" Icon="bxs-microchip"/>
|
||||
|
|
|
@ -78,7 +78,11 @@
|
|||
@if (NodeStats.ContainsKey(context!.Id) && NodeStats[context.Id] != null)
|
||||
{
|
||||
var memory = NodeStats[context!.Id]!.Hardware.Memory;
|
||||
var percent = Math.Round(((float)memory.Total - memory.Available - memory.Free) / memory.Total * 100, 2);
|
||||
|
||||
var used = memory.Total - (memory.Available + memory.Cached);
|
||||
var percent = Math.Round((float) used / memory.Total * 100F, 2);
|
||||
|
||||
//Logger.Debug($"Used: {used} Total: {memory.Total} => {percent}% ({Formatter.FormatSize(used)} / {Formatter.FormatSize(memory.Total)})");
|
||||
|
||||
<ColoredBar Value="percent"/>
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
|
@ -53,6 +53,7 @@
|
|||
<Folder Include="Core\Database\Migrations\" />
|
||||
<Folder Include="Core\Http\Requests\" />
|
||||
<Folder Include="Core\Http\Resources\" />
|
||||
<Folder Include="Core\Implementations\UI\Admin\AdminComponents\" />
|
||||
<Folder Include="Core\UI\Components\Forms\" />
|
||||
<Folder Include="Features\Dummy\Configuration\" />
|
||||
<Folder Include="Features\Dummy\Entities\" />
|
||||
|
@ -74,6 +75,7 @@
|
|||
<Folder Include="Features\FileManager\Http\Requests\" />
|
||||
<Folder Include="Features\FileManager\Http\Resources\" />
|
||||
<Folder Include="Features\Servers\Http\Resources\" />
|
||||
<Folder Include="Features\Servers\Implementations\UI\Admin\" />
|
||||
<Folder Include="storage\" />
|
||||
<Folder Include="Styles\" />
|
||||
<Folder Include="wwwroot\css\" />
|
||||
|
@ -89,31 +91,11 @@
|
|||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MoonCore" Version="1.2.7" />
|
||||
<PackageReference Include="MoonCoreUI" Version="1.1.6" />
|
||||
<PackageReference Include="MoonCore" Version="1.3.3" />
|
||||
<PackageReference Include="MoonCoreUI" Version="1.1.7" />
|
||||
<PackageReference Include="Otp.NET" Version="1.3.0" />
|
||||
<PackageReference Include="QRCoder" Version="1.4.3" />
|
||||
<PackageReference Include="XtermBlazor" Version="1.10.2" />
|
||||
<PackageReference Include="Z.Blazor.Diagrams" Version="3.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<_ContentIncludedByDefault Remove="wwwroot\fonts\boxicons.eot" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\fonts\boxicons.svg" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\fonts\boxicons.ttf" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\fonts\boxicons.woff" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\fonts\boxicons.woff2" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\fonts\Inter.woff2" />
|
||||
<_ContentIncludedByDefault Remove="Features\ScheduleDesigner\UI\Components\ScheduleEditor.razor" />
|
||||
<_ContentIncludedByDefault Remove="Features\ScheduleDesigner\UI\Components\ScheduleLinkItem.razor" />
|
||||
<_ContentIncludedByDefault Remove="Features\ScheduleDesigner\UI\Components\ScheduleNodeItem.razor" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\svg\logo.svg" />
|
||||
<_ContentIncludedByDefault Remove="Features\FileManager\UI\Components\Editor.razor" />
|
||||
<_ContentIncludedByDefault Remove="Features\FileManager\UI\Components\FileEditor.razor" />
|
||||
<_ContentIncludedByDefault Remove="Features\FileManager\UI\Components\FileManager.razor" />
|
||||
<_ContentIncludedByDefault Remove="Features\FileManager\UI\Components\FileUploader.razor" />
|
||||
<_ContentIncludedByDefault Remove="Features\FileManager\UI\Components\FileView.razor" />
|
||||
<_ContentIncludedByDefault Remove="storage\configs\core.json" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
<title>Moonlight</title>
|
||||
|
||||
<link rel="shortcut icon" href="/img/logo.svg">
|
||||
<link rel="shortcut icon" href="/api/core/asset/Core/svg/logo.svg">
|
||||
|
||||
<!-- Assets (css) -->
|
||||
@foreach (var asset in FeatureService.PreInitContext.Assets)
|
||||
|
|
|
@ -6,6 +6,7 @@ using Moonlight.Core.Configuration;
|
|||
using Moonlight.Core.Database;
|
||||
using Moonlight.Core.Http.Middleware;
|
||||
using Moonlight.Core.Services;
|
||||
using MySqlConnector;
|
||||
|
||||
// Create needed storage directories
|
||||
Directory.CreateDirectory(PathBuilder.Dir("storage"));
|
||||
|
@ -74,11 +75,29 @@ await featureService.Load();
|
|||
var pluginService = new PluginService();
|
||||
await pluginService.Load();
|
||||
|
||||
// Check database migrations
|
||||
await DatabaseCheckHelper.Check(
|
||||
new DataContext(configService),
|
||||
false
|
||||
);
|
||||
try
|
||||
{
|
||||
// Check database migrations
|
||||
await DatabaseCheckHelper.Check(
|
||||
new DataContext(configService),
|
||||
false
|
||||
);
|
||||
}
|
||||
catch (MySqlException e)
|
||||
{
|
||||
if (e.InnerException is EndOfStreamException eosException)
|
||||
{
|
||||
if (eosException.Message.Contains("read 4 header bytes"))
|
||||
{
|
||||
Logger.Warn("The mysql server appears to be still booting up. Exiting...");
|
||||
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
// Add pre constructed services
|
||||
builder.Services.AddSingleton(featureService);
|
||||
|
|
|
@ -66,4 +66,5 @@ Distributed under the CC0 1.0 Universal License. See [LICENSE](https://github.co
|
|||
|
||||
## Authors
|
||||
|
||||
* **Masu Baumgartner** - *Endelon Hosting* - [Masu Baumgartner](https://github.com/Marcel-Baumgartner) - *Moonlights core system & frontend and basiclly any other parts of moonlight*
|
||||
* **Masu Baumgartner** - [Masu Baumgartner](https://github.com/Masu-Baumgartner) - *Moonlights core system & frontend and basiclly any other part of moonlight*
|
||||
* **Moritz Deiaco** - [Moritz Deiaco](https://github.com/Moritz-Deiaco) - *Moonlight Core and UI*
|
||||
|
|
Loading…
Add table
Reference in a new issue