Merge pull request #209 from Moonlight-Panel/NewVisualConfigEditor
Added a new visual config editor
This commit is contained in:
commit
d1c9009e9f
9 changed files with 442 additions and 42 deletions
|
@ -1,19 +1,25 @@
|
|||
namespace Moonlight.App.Configuration;
|
||||
using System.ComponentModel;
|
||||
using Moonlight.App.Helpers;
|
||||
|
||||
namespace Moonlight.App.Configuration;
|
||||
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
public class ConfigV1
|
||||
{
|
||||
[JsonProperty("Moonlight")] public MoonlightData Moonlight { get; set; } = new();
|
||||
[JsonProperty("Moonlight")]
|
||||
public MoonlightData Moonlight { get; set; } = new();
|
||||
|
||||
public class MoonlightData
|
||||
{
|
||||
[JsonProperty("AppUrl")] public string AppUrl { get; set; } = "http://your-moonlight-url-without-slash";
|
||||
[JsonProperty("AppUrl")]
|
||||
[Description("The url moonlight is accesible with from the internet")]
|
||||
public string AppUrl { get; set; } = "http://your-moonlight-url-without-slash";
|
||||
|
||||
[JsonProperty("Database")] public DatabaseData Database { get; set; } = new();
|
||||
|
||||
[JsonProperty("DiscordBotApi")] public DiscordBotData DiscordBotApi { get; set; } = new();
|
||||
[JsonProperty("DiscordBotApi")] public DiscordBotApiData DiscordBotApi { get; set; } = new();
|
||||
|
||||
[JsonProperty("DiscordBot")] public DiscordBotData DiscordBot { get; set; } = new();
|
||||
|
||||
|
@ -47,17 +53,29 @@ public class ConfigV1
|
|||
|
||||
public class CleanupData
|
||||
{
|
||||
[JsonProperty("Cpu")] public long Cpu { get; set; } = 90;
|
||||
[JsonProperty("Cpu")]
|
||||
[Description("The maximum amount of cpu usage in percent a node is allowed to use before the cleanup starts")]
|
||||
public long Cpu { get; set; } = 90;
|
||||
|
||||
[JsonProperty("Memory")] public long Memory { get; set; } = 8192;
|
||||
[JsonProperty("Memory")]
|
||||
[Description("The minumum amount of memory in megabytes avaliable before the cleanup starts")]
|
||||
public long Memory { get; set; } = 8192;
|
||||
|
||||
[JsonProperty("Wait")] public long Wait { get; set; } = 15;
|
||||
[JsonProperty("Wait")]
|
||||
[Description("The delay between every cleanup check in minutes")]
|
||||
public long Wait { get; set; } = 15;
|
||||
|
||||
[JsonProperty("Uptime")] public long Uptime { get; set; } = 6;
|
||||
[JsonProperty("Uptime")]
|
||||
[Description("The maximum uptime of any server in hours before it the server restarted by the cleanup system")]
|
||||
public long Uptime { get; set; } = 6;
|
||||
|
||||
[JsonProperty("Enable")] public bool Enable { get; set; } = false;
|
||||
[JsonProperty("Enable")]
|
||||
[Description("The cleanup system provides a fair way for stopping unused servers and staying stable even with overallocation. A detailed explanation: docs.endelon-hosting.de/erklaerungen/cleanup")]
|
||||
public bool Enable { get; set; } = false;
|
||||
|
||||
[JsonProperty("MinUptime")] public long MinUptime { get; set; } = 10;
|
||||
[JsonProperty("MinUptime")]
|
||||
[Description("The minumum uptime of a server in minutes to prevent stopping servers which just started")]
|
||||
public long MinUptime { get; set; } = 10;
|
||||
}
|
||||
|
||||
public class DatabaseData
|
||||
|
@ -66,38 +84,76 @@ public class ConfigV1
|
|||
|
||||
[JsonProperty("Host")] public string Host { get; set; } = "your.database.host";
|
||||
|
||||
[JsonProperty("Password")] public string Password { get; set; } = "secret";
|
||||
[JsonProperty("Password")]
|
||||
[Blur]
|
||||
public string Password { get; set; } = "secret";
|
||||
|
||||
[JsonProperty("Port")] public long Port { get; set; } = 3306;
|
||||
|
||||
[JsonProperty("Username")] public string Username { get; set; } = "moonlight_user";
|
||||
}
|
||||
|
||||
public class DiscordBotApiData
|
||||
{
|
||||
[JsonProperty("Enable")]
|
||||
[Description("Enable the discord bot api. Currently only DatBot is using this api")]
|
||||
public bool Enable { get; set; } = false;
|
||||
|
||||
[JsonProperty("Token")]
|
||||
[Description("Specify the token the api client needs to provide")]
|
||||
[Blur]
|
||||
public string Token { get; set; } = Guid.NewGuid().ToString();
|
||||
}
|
||||
public class DiscordBotData
|
||||
{
|
||||
[JsonProperty("Enable")] public bool Enable { get; set; } = false;
|
||||
[JsonProperty("Enable")]
|
||||
[Description("The discord bot can be used to allow customers to manage their servers via discord")]
|
||||
public bool Enable { get; set; } = false;
|
||||
|
||||
[JsonProperty("Token")] public string Token { get; set; } = "discord token here";
|
||||
[JsonProperty("Token")]
|
||||
[Description("Your discord bot token goes here")]
|
||||
[Blur]
|
||||
public string Token { get; set; } = "discord token here";
|
||||
|
||||
[JsonProperty("PowerActions")] public bool PowerActions { get; set; } = false;
|
||||
[JsonProperty("SendCommands")] public bool SendCommands { get; set; } = false;
|
||||
[JsonProperty("PowerActions")]
|
||||
[Description("Enable actions like starting and stopping servers")]
|
||||
public bool PowerActions { get; set; } = false;
|
||||
|
||||
[JsonProperty("SendCommands")]
|
||||
[Description("Allow users to send commands to their servers")]
|
||||
public bool SendCommands { get; set; } = false;
|
||||
}
|
||||
|
||||
public class DiscordNotificationsData
|
||||
{
|
||||
[JsonProperty("Enable")] public bool Enable { get; set; } = false;
|
||||
[JsonProperty("Enable")]
|
||||
[Description("The discord notification system sends you a message everytime a event like a new support chat message is triggered with usefull data describing the event")]
|
||||
public bool Enable { get; set; } = false;
|
||||
|
||||
[JsonProperty("WebHook")] public string WebHook { get; set; } = "http://your-discord-webhook-url";
|
||||
[JsonProperty("WebHook")]
|
||||
[Description("The discord webhook the notifications are being sent to")]
|
||||
[Blur]
|
||||
public string WebHook { get; set; } = "http://your-discord-webhook-url";
|
||||
}
|
||||
|
||||
public class DomainsData
|
||||
{
|
||||
[JsonProperty("Enable")] public bool Enable { get; set; } = false;
|
||||
[JsonProperty("AccountId")] public string AccountId { get; set; } = "cloudflare acc id";
|
||||
[JsonProperty("Enable")]
|
||||
[Description("This enables the domain system")]
|
||||
public bool Enable { get; set; } = false;
|
||||
|
||||
[JsonProperty("Email")] public string Email { get; set; } = "cloudflare@acc.email";
|
||||
[JsonProperty("AccountId")]
|
||||
[Description("This option specifies the cloudflare account id")]
|
||||
public string AccountId { get; set; } = "cloudflare acc id";
|
||||
|
||||
[JsonProperty("Key")] public string Key { get; set; } = "secret";
|
||||
[JsonProperty("Email")]
|
||||
[Description("This specifies the cloudflare email to use for communicating with the cloudflare api")]
|
||||
public string Email { get; set; } = "cloudflare@acc.email";
|
||||
|
||||
[JsonProperty("Key")]
|
||||
[Description("Your cloudflare api key goes here")]
|
||||
[Blur]
|
||||
public string Key { get; set; } = "secret";
|
||||
}
|
||||
|
||||
public class HtmlData
|
||||
|
@ -107,13 +163,21 @@ public class ConfigV1
|
|||
|
||||
public class HeadersData
|
||||
{
|
||||
[JsonProperty("Color")] public string Color { get; set; } = "#4b27e8";
|
||||
[JsonProperty("Color")]
|
||||
[Description("This specifies the color of the embed generated by platforms like discord when someone posts a link to your moonlight instance")]
|
||||
public string Color { get; set; } = "#4b27e8";
|
||||
|
||||
[JsonProperty("Description")] public string Description { get; set; } = "the next generation hosting panel";
|
||||
[JsonProperty("Description")]
|
||||
[Description("This specifies the description text of the embed generated by platforms like discord when someone posts a link to your moonlight instance and can also help google to index your moonlight instance correctly")]
|
||||
public string Description { get; set; } = "the next generation hosting panel";
|
||||
|
||||
[JsonProperty("Keywords")] public string Keywords { get; set; } = "moonlight";
|
||||
[JsonProperty("Keywords")]
|
||||
[Description("To help search engines like google to index your moonlight instance correctly you can specify keywords seperated by a comma here")]
|
||||
public string Keywords { get; set; } = "moonlight";
|
||||
|
||||
[JsonProperty("Title")] public string Title { get; set; } = "Moonlight - endelon.link";
|
||||
[JsonProperty("Title")]
|
||||
[Description("This specifies the title of the embed generated by platforms like discord when someone posts a link to your moonlight instance")]
|
||||
public string Title { get; set; } = "Moonlight - endelon.link";
|
||||
}
|
||||
|
||||
public class MailData
|
||||
|
@ -122,7 +186,9 @@ public class ConfigV1
|
|||
|
||||
[JsonProperty("Server")] public string Server { get; set; } = "your.mail.host";
|
||||
|
||||
[JsonProperty("Password")] public string Password { get; set; } = "secret";
|
||||
[JsonProperty("Password")]
|
||||
[Blur]
|
||||
public string Password { get; set; } = "secret";
|
||||
|
||||
[JsonProperty("Port")] public int Port { get; set; } = 465;
|
||||
|
||||
|
@ -142,9 +208,13 @@ public class ConfigV1
|
|||
|
||||
public class OAuth2Data
|
||||
{
|
||||
[JsonProperty("OverrideUrl")] public string OverrideUrl { get; set; } = "https://only-for-development.cases";
|
||||
[JsonProperty("OverrideUrl")]
|
||||
[Description("This overrides the redirect url which would be typicaly the app url")]
|
||||
public string OverrideUrl { get; set; } = "https://only-for-development.cases";
|
||||
|
||||
[JsonProperty("EnableOverrideUrl")] public bool EnableOverrideUrl { get; set; } = false;
|
||||
[JsonProperty("EnableOverrideUrl")]
|
||||
[Description("This enables the url override")]
|
||||
public bool EnableOverrideUrl { get; set; } = false;
|
||||
|
||||
[JsonProperty("Providers")]
|
||||
public OAuth2ProviderData[] Providers { get; set; } = Array.Empty<OAuth2ProviderData>();
|
||||
|
@ -156,41 +226,65 @@ public class ConfigV1
|
|||
|
||||
[JsonProperty("ClientId")] public string ClientId { get; set; }
|
||||
|
||||
[JsonProperty("ClientSecret")] public string ClientSecret { get; set; }
|
||||
[JsonProperty("ClientSecret")]
|
||||
[Blur]
|
||||
public string ClientSecret { get; set; }
|
||||
}
|
||||
|
||||
public class RatingData
|
||||
{
|
||||
[JsonProperty("Enabled")] public bool Enabled { get; set; } = false;
|
||||
[JsonProperty("Enabled")]
|
||||
[Description("The rating systems shows a user who is registered longer than the set amout of days a popup to rate this platform if he hasnt rated it before")]
|
||||
public bool Enabled { get; set; } = false;
|
||||
|
||||
[JsonProperty("Url")] public string Url { get; set; } = "https://link-to-google-or-smth";
|
||||
[JsonProperty("Url")]
|
||||
[Description("This is the url a user who rated above a set limit is shown to rate you again. Its recommended to put your google or trustpilot rate link here")]
|
||||
public string Url { get; set; } = "https://link-to-google-or-smth";
|
||||
|
||||
[JsonProperty("MinRating")] public int MinRating { get; set; } = 4;
|
||||
[JsonProperty("MinRating")]
|
||||
[Description("The minimum star count on the rating ranging from 1 to 5")]
|
||||
public int MinRating { get; set; } = 4;
|
||||
|
||||
[JsonProperty("DaysSince")] public int DaysSince { get; set; } = 5;
|
||||
[JsonProperty("DaysSince")]
|
||||
[Description("The days a user has to be registered to even be able to get this popup")]
|
||||
public int DaysSince { get; set; } = 5;
|
||||
}
|
||||
|
||||
public class SecurityData
|
||||
{
|
||||
[JsonProperty("Token")] public string Token { get; set; } = Guid.NewGuid().ToString();
|
||||
[JsonProperty("Token")]
|
||||
[Description("This is the moonlight app token. It is used to encrypt and decrypt data and validte tokens and sessions")]
|
||||
[Blur]
|
||||
public string Token { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
[JsonProperty("ReCaptcha")] public ReCaptchaData ReCaptcha { get; set; } = new();
|
||||
}
|
||||
|
||||
public class ReCaptchaData
|
||||
{
|
||||
[JsonProperty("Enable")] public bool Enable { get; set; } = false;
|
||||
[JsonProperty("Enable")]
|
||||
[Description("Enables repatcha at places like the register page. For information how to get your recaptcha credentails go to google.com/recaptcha/about/")]
|
||||
public bool Enable { get; set; } = false;
|
||||
|
||||
[JsonProperty("SiteKey")] public string SiteKey { get; set; } = "recaptcha site key here";
|
||||
[JsonProperty("SiteKey")]
|
||||
[Blur]
|
||||
public string SiteKey { get; set; } = "recaptcha site key here";
|
||||
|
||||
[JsonProperty("SecretKey")] public string SecretKey { get; set; } = "recaptcha secret here";
|
||||
[JsonProperty("SecretKey")]
|
||||
[Blur]
|
||||
public string SecretKey { get; set; } = "recaptcha secret here";
|
||||
}
|
||||
|
||||
public class SentryData
|
||||
{
|
||||
[JsonProperty("Enable")] public bool Enable { get; set; } = false;
|
||||
[JsonProperty("Enable")]
|
||||
[Description("Sentry is a way to monitor application crashes and performance issues in real time. Enable this option only if you set a sentry dsn")]
|
||||
public bool Enable { get; set; } = false;
|
||||
|
||||
[JsonProperty("Dsn")] public string Dsn { get; set; } = "http://your-sentry-url-here";
|
||||
[JsonProperty("Dsn")]
|
||||
[Description("The dsn is the key moonlight needs to communicate with your sentry instance")]
|
||||
[Blur]
|
||||
public string Dsn { get; set; } = "http://your-sentry-url-here";
|
||||
}
|
||||
|
||||
public class SmartDeployData
|
||||
|
|
6
Moonlight/App/Helpers/BlurAttribute.cs
Normal file
6
Moonlight/App/Helpers/BlurAttribute.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Moonlight.App.Helpers;
|
||||
|
||||
public class BlurAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
|
@ -1,9 +1,36 @@
|
|||
using Moonlight.App.Services;
|
||||
using System.Text;
|
||||
using Moonlight.App.Services;
|
||||
|
||||
namespace Moonlight.App.Helpers;
|
||||
|
||||
public static class Formatter
|
||||
{
|
||||
public static string ReplaceEnd(string input, string substringToReplace, string newSubstring)
|
||||
{
|
||||
int lastIndexOfSubstring = input.LastIndexOf(substringToReplace);
|
||||
if (lastIndexOfSubstring >= 0)
|
||||
{
|
||||
input = input.Remove(lastIndexOfSubstring, substringToReplace.Length).Insert(lastIndexOfSubstring, newSubstring);
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
public static string ConvertCamelCaseToSpaces(string input)
|
||||
{
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
foreach (char c in input)
|
||||
{
|
||||
if (char.IsUpper(c))
|
||||
{
|
||||
output.Append(' ');
|
||||
}
|
||||
|
||||
output.Append(c);
|
||||
}
|
||||
|
||||
return output.ToString().Trim();
|
||||
}
|
||||
public static string FormatUptime(double uptime)
|
||||
{
|
||||
TimeSpan t = TimeSpan.FromMilliseconds(uptime);
|
||||
|
|
51
Moonlight/App/Helpers/PropBinder.cs
Normal file
51
Moonlight/App/Helpers/PropBinder.cs
Normal file
|
@ -0,0 +1,51 @@
|
|||
using System.Reflection;
|
||||
|
||||
namespace Moonlight.App.Helpers;
|
||||
|
||||
public class PropBinder
|
||||
{
|
||||
private PropertyInfo PropertyInfo;
|
||||
private object DataObject;
|
||||
|
||||
public PropBinder(PropertyInfo propertyInfo, object dataObject)
|
||||
{
|
||||
PropertyInfo = propertyInfo;
|
||||
DataObject = dataObject;
|
||||
}
|
||||
|
||||
public string StringValue
|
||||
{
|
||||
get => (string)PropertyInfo.GetValue(DataObject)!;
|
||||
set => PropertyInfo.SetValue(DataObject, value);
|
||||
}
|
||||
|
||||
public int IntValue
|
||||
{
|
||||
get => (int)PropertyInfo.GetValue(DataObject)!;
|
||||
set => PropertyInfo.SetValue(DataObject, value);
|
||||
}
|
||||
|
||||
public long LongValue
|
||||
{
|
||||
get => (long)PropertyInfo.GetValue(DataObject)!;
|
||||
set => PropertyInfo.SetValue(DataObject, value);
|
||||
}
|
||||
|
||||
public bool BoolValue
|
||||
{
|
||||
get => (bool)PropertyInfo.GetValue(DataObject)!;
|
||||
set => PropertyInfo.SetValue(DataObject, value);
|
||||
}
|
||||
|
||||
public DateTime DateTimeValue
|
||||
{
|
||||
get => (DateTime)PropertyInfo.GetValue(DataObject)!;
|
||||
set => PropertyInfo.SetValue(DataObject, value);
|
||||
}
|
||||
|
||||
public double DoubleValue
|
||||
{
|
||||
get => (double)PropertyInfo.GetValue(DataObject)!;
|
||||
set => PropertyInfo.SetValue(DataObject, value);
|
||||
}
|
||||
}
|
|
@ -51,7 +51,27 @@ public class ConfigService
|
|||
File.ReadAllText(path)
|
||||
) ?? new ConfigV1();
|
||||
|
||||
File.WriteAllText(path, JsonConvert.SerializeObject(Configuration));
|
||||
File.WriteAllText(path, JsonConvert.SerializeObject(Configuration, Formatting.Indented));
|
||||
}
|
||||
|
||||
public void Save(ConfigV1 configV1)
|
||||
{
|
||||
Configuration = configV1;
|
||||
Save();
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var path = PathBuilder.File("storage", "configs", "config.json");
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
File.WriteAllText(path, "{}");
|
||||
}
|
||||
|
||||
File.WriteAllText(path, JsonConvert.SerializeObject(Configuration, Formatting.Indented));
|
||||
|
||||
Reload();
|
||||
}
|
||||
|
||||
public ConfigV1 Get()
|
||||
|
|
65
Moonlight/Shared/Components/Forms/SmartFormClass.razor
Normal file
65
Moonlight/Shared/Components/Forms/SmartFormClass.razor
Normal file
|
@ -0,0 +1,65 @@
|
|||
@using System.Reflection
|
||||
@using System.Collections
|
||||
@using Moonlight.App.Helpers
|
||||
|
||||
<div class="accordion my-3" id="configSetting@(Model.GetHashCode())">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="configSetting-header@(Model.GetHashCode())">
|
||||
<button class="accordion-button fs-4 fw-semibold collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#configSetting-body@(Model.GetHashCode())" aria-expanded="false" aria-controls="configSetting-body@(Model.GetHashCode())">
|
||||
@{
|
||||
var name = Formatter.ReplaceEnd(Model.GetType().Name, "Data", "");
|
||||
name = Formatter.ConvertCamelCaseToSpaces(name);
|
||||
}
|
||||
|
||||
@(name)
|
||||
</button>
|
||||
</h2>
|
||||
<div id="configSetting-body@(Model.GetHashCode())" class="accordion-collapse collapse" aria-labelledby="configSetting-header@(Model.GetHashCode())" data-bs-parent="#configSetting">
|
||||
<div class="accordion-body">
|
||||
@foreach (var property in Model.GetType().GetProperties())
|
||||
{
|
||||
@BindAndRenderProperty(property)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public object Model { get; set; }
|
||||
|
||||
private RenderFragment BindAndRenderProperty(PropertyInfo property)
|
||||
{
|
||||
if (property.PropertyType.IsClass && !property.PropertyType.IsPrimitive && !typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
|
||||
{
|
||||
return @<SmartFormClass Model="@property.GetValue(Model)"/>;
|
||||
|
||||
// If the property is a subclass, serialize and generate form for it
|
||||
/*
|
||||
foreach (var subProperty in property.PropertyType.GetProperties())
|
||||
{
|
||||
return BindAndRenderProperty(subProperty);
|
||||
}*/
|
||||
}
|
||||
else if (property.PropertyType == typeof(int) || property.PropertyType == typeof(string) || property.PropertyType == typeof(bool) || property.PropertyType == typeof(decimal) || property.PropertyType == typeof(long))
|
||||
{
|
||||
return @<SmartFormProperty Model="Model" PropertyInfo="property"/>;
|
||||
}
|
||||
else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
|
||||
{
|
||||
// If the property is a collection, generate form for each element
|
||||
var collection = property.GetValue(Model) as IEnumerable;
|
||||
if (collection != null)
|
||||
{
|
||||
foreach (var element in collection)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
// Additional property types could be handled here (e.g., DateTime, int, etc.)
|
||||
|
||||
return @<div></div>;
|
||||
}
|
||||
}
|
79
Moonlight/Shared/Components/Forms/SmartFormProperty.razor
Normal file
79
Moonlight/Shared/Components/Forms/SmartFormProperty.razor
Normal file
|
@ -0,0 +1,79 @@
|
|||
@using System.Reflection
|
||||
@using Moonlight.App.Helpers
|
||||
@using System.ComponentModel
|
||||
|
||||
<label class="form-label" for="@PropertyInfo.Name">
|
||||
@(Formatter.ConvertCamelCaseToSpaces(PropertyInfo.Name))
|
||||
</label>
|
||||
@{
|
||||
//TODO: Tidy up this code
|
||||
|
||||
var attrs = PropertyInfo.GetCustomAttributes(true);
|
||||
|
||||
var descAttr = attrs
|
||||
.FirstOrDefault(x => x.GetType() == typeof(DescriptionAttribute));
|
||||
|
||||
var blurBool = attrs.Any(x => x.GetType() == typeof(BlurAttribute));
|
||||
var blur = blurBool ? "blur-unless-hover" : "";
|
||||
}
|
||||
|
||||
@if (descAttr != null)
|
||||
{
|
||||
var a = descAttr as DescriptionAttribute;
|
||||
|
||||
<div class="form-text fs-5 mb-2 mt-0">
|
||||
@(a.Description)
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="input-group mb-5">
|
||||
@if (PropertyInfo.PropertyType == typeof(string))
|
||||
{
|
||||
var binder = new PropBinder(PropertyInfo, Model!);
|
||||
|
||||
<div class="@(blur) w-100">
|
||||
<InputText id="@PropertyInfo.Name" @bind-Value="binder.StringValue" class="form-control"/>
|
||||
</div>
|
||||
}
|
||||
else if (PropertyInfo.PropertyType == typeof(int))
|
||||
{
|
||||
var binder = new PropBinder(PropertyInfo, Model!);
|
||||
|
||||
<InputNumber id="@PropertyInfo.Name" @bind-Value="binder.IntValue" class="form-control"/>
|
||||
}
|
||||
else if (PropertyInfo.PropertyType == typeof(long))
|
||||
{
|
||||
var binder = new PropBinder(PropertyInfo, Model!);
|
||||
|
||||
<InputNumber id="@PropertyInfo.Name" @bind-Value="binder.LongValue" class="form-control"/>
|
||||
}
|
||||
else if (PropertyInfo.PropertyType == typeof(bool))
|
||||
{
|
||||
var binder = new PropBinder(PropertyInfo, Model!);
|
||||
|
||||
<div class="form-check">
|
||||
<InputCheckbox id="@PropertyInfo.Name" @bind-Value="binder.BoolValue" class="form-check-input"/>
|
||||
</div>
|
||||
}
|
||||
else if (PropertyInfo.PropertyType == typeof(DateTime))
|
||||
{
|
||||
var binder = new PropBinder(PropertyInfo, Model!);
|
||||
|
||||
<InputDate id="@PropertyInfo.Name" @bind-Value="binder.DateTimeValue" class="form-control"/>
|
||||
}
|
||||
else if (PropertyInfo.PropertyType == typeof(decimal))
|
||||
{
|
||||
var binder = new PropBinder(PropertyInfo, Model!);
|
||||
|
||||
<InputNumber id="@PropertyInfo.Name" step="0.01" @bind-Value="binder.DoubleValue" class="form-control"/>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public PropertyInfo PropertyInfo { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public object Model { get; set; }
|
||||
}
|
|
@ -39,6 +39,11 @@
|
|||
<TL>News</TL>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item mt-2">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 8 ? "active" : "")" href="/admin/system/configuration">
|
||||
<TL>Configuration</TL>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
53
Moonlight/Shared/Views/Admin/Sys/Configuration.razor
Normal file
53
Moonlight/Shared/Views/Admin/Sys/Configuration.razor
Normal file
|
@ -0,0 +1,53 @@
|
|||
@page "/admin/system/configuration"
|
||||
|
||||
@using Moonlight.App.Services
|
||||
@using Moonlight.Shared.Components.Navigations
|
||||
@using Moonlight.App.Configuration
|
||||
@using Moonlight.App.Services.Interop
|
||||
|
||||
@inject ConfigService ConfigService
|
||||
@inject ToastService ToastService
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
|
||||
<OnlyAdmin>
|
||||
<AdminSystemNavigation Index="8"/>
|
||||
|
||||
<LazyLoader Load="Load">
|
||||
<div class="card">
|
||||
<SmartForm Model="Config" OnValidSubmit="OnSubmit">
|
||||
<div class="card-body">
|
||||
<SmartFormClass Model="Config"/>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="text-end">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<TL>Save</TL>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</SmartForm>
|
||||
</div>
|
||||
</LazyLoader>
|
||||
</OnlyAdmin>
|
||||
|
||||
@code
|
||||
{
|
||||
private ConfigV1 Config;
|
||||
|
||||
private Task Load(LazyLoader lazyLoader)
|
||||
{
|
||||
Config = ConfigService.Get();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task OnSubmit()
|
||||
{
|
||||
ConfigService.Save(Config);
|
||||
await ToastService.Success(
|
||||
SmartTranslateService.Translate(
|
||||
"Successfully saved and reloaded configuration. Some changes may take affect after a restart of moonlight"
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue