10 Commits
v1b11 ... v1b12

Author SHA1 Message Date
Marcel Baumgartner
2fe17473ae Merge pull request #204 from Moonlight-Panel/SwitchToNewConfigSystem
Switched to new config system
2023-07-02 02:19:32 +02:00
Marcel Baumgartner
609cf8cfac Switched to new config system 2023-07-02 02:16:44 +02:00
Marcel Baumgartner
678da30b09 Merge pull request #203 from Moonlight-Panel/LogsAndFixes
Added new log files and log migrators. Fixed some errors with js invokes
2023-07-02 00:21:55 +02:00
Marcel Baumgartner
d19412f4bb Added new log files and log migrators. Fixed some errors with js invokes 2023-07-02 00:21:35 +02:00
Marcel Baumgartner
1665d6e537 Merge pull request #202 from Moonlight-Panel/AddSentrySupport
Added new sentry support
2023-07-01 19:01:00 +02:00
Marcel Baumgartner
fd210f2404 Added new sentry support 2023-07-01 19:00:38 +02:00
Marcel Baumgartner
c33729fb44 Merge pull request #201 from Moonlight-Panel/FixServerList
Fixed server list
2023-06-30 21:55:58 +02:00
Marcel Baumgartner
7983bf3ee4 Fixed server list 2023-06-30 21:55:32 +02:00
Marcel Baumgartner
a09f60aea7 Merge pull request #200 from Moonlight-Panel/HttpTimeoutFixes
Fixed timeout options from assumed seconds to real value miliseconds
2023-06-29 23:28:07 +02:00
Marcel Baumgartner
28b5893c21 Fixed timeout options from assumed seconds to real value miliseconds 2023-06-29 23:27:48 +02:00
50 changed files with 836 additions and 571 deletions

View File

@@ -47,7 +47,7 @@ public class ModrinthApiHelper
var request = new RestRequest(url) var request = new RestRequest(url)
{ {
Timeout = 60 * 15 Timeout = 300000
}; };
request.AddHeader("Content-Type", "application/json"); request.AddHeader("Content-Type", "application/json");

View File

@@ -209,7 +209,7 @@ public class WingsApiHelper
var request = new RestRequest(url) var request = new RestRequest(url)
{ {
Timeout = 60 * 15 Timeout = 300000
}; };
request.AddHeader("Content-Type", "application/json"); request.AddHeader("Content-Type", "application/json");

View File

@@ -0,0 +1,226 @@
namespace Moonlight.App.Configuration;
using System;
using Newtonsoft.Json;
public class ConfigV1
{
[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("Database")] public DatabaseData Database { get; set; } = new();
[JsonProperty("DiscordBotApi")] public DiscordBotData DiscordBotApi { get; set; } = new();
[JsonProperty("DiscordBot")] public DiscordBotData DiscordBot { get; set; } = new();
[JsonProperty("Domains")] public DomainsData Domains { get; set; } = new();
[JsonProperty("Html")] public HtmlData Html { get; set; } = new();
[JsonProperty("Marketing")] public MarketingData Marketing { get; set; } = new();
[JsonProperty("OAuth2")] public OAuth2Data OAuth2 { get; set; } = new();
[JsonProperty("Security")] public SecurityData Security { get; set; } = new();
[JsonProperty("Mail")] public MailData Mail { get; set; } = new();
[JsonProperty("Cleanup")] public CleanupData Cleanup { get; set; } = new();
[JsonProperty("Subscriptions")] public SubscriptionsData Subscriptions { get; set; } = new();
[JsonProperty("DiscordNotifications")]
public DiscordNotificationsData DiscordNotifications { get; set; } = new();
[JsonProperty("Statistics")] public StatisticsData Statistics { get; set; } = new();
[JsonProperty("Rating")] public RatingData Rating { get; set; } = new();
[JsonProperty("SmartDeploy")] public SmartDeployData SmartDeploy { get; set; } = new();
[JsonProperty("Sentry")] public SentryData Sentry { get; set; } = new();
}
public class CleanupData
{
[JsonProperty("Cpu")] public long Cpu { get; set; } = 90;
[JsonProperty("Memory")] public long Memory { get; set; } = 8192;
[JsonProperty("Wait")] public long Wait { get; set; } = 15;
[JsonProperty("Uptime")] public long Uptime { get; set; } = 6;
[JsonProperty("Enable")] public bool Enable { get; set; } = false;
[JsonProperty("MinUptime")] public long MinUptime { get; set; } = 10;
}
public class DatabaseData
{
[JsonProperty("Database")] public string Database { get; set; } = "moonlight_db";
[JsonProperty("Host")] public string Host { get; set; } = "your.database.host";
[JsonProperty("Password")] 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 DiscordBotData
{
[JsonProperty("Enable")] public bool Enable { get; set; } = false;
[JsonProperty("Token")] public string Token { get; set; } = "discord token here";
[JsonProperty("PowerActions")] public bool PowerActions { get; set; } = false;
[JsonProperty("SendCommands")] public bool SendCommands { get; set; } = false;
}
public class DiscordNotificationsData
{
[JsonProperty("Enable")] public bool Enable { get; set; } = false;
[JsonProperty("WebHook")] 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("Email")] public string Email { get; set; } = "cloudflare@acc.email";
[JsonProperty("Key")] public string Key { get; set; } = "secret";
}
public class HtmlData
{
[JsonProperty("Headers")] public HeadersData Headers { get; set; } = new();
}
public class HeadersData
{
[JsonProperty("Color")] public string Color { get; set; } = "#4b27e8";
[JsonProperty("Description")] public string Description { get; set; } = "the next generation hosting panel";
[JsonProperty("Keywords")] public string Keywords { get; set; } = "moonlight";
[JsonProperty("Title")] public string Title { get; set; } = "Moonlight - endelon.link";
}
public class MailData
{
[JsonProperty("Email")] public string Email { get; set; } = "username@your.mail.host";
[JsonProperty("Server")] public string Server { get; set; } = "your.mail.host";
[JsonProperty("Password")] public string Password { get; set; } = "secret";
[JsonProperty("Port")] public int Port { get; set; } = 465;
[JsonProperty("Ssl")] public bool Ssl { get; set; } = true;
}
public class MarketingData
{
[JsonProperty("BrandName")] public string BrandName { get; set; } = "Endelon Hosting";
[JsonProperty("Imprint")] public string Imprint { get; set; } = "https://your-site.xyz/imprint";
[JsonProperty("Privacy")] public string Privacy { get; set; } = "https://your-site.xyz/privacy";
[JsonProperty("About")] public string About { get; set; } = "https://your-site.xyz/about";
[JsonProperty("Website")] public string Website { get; set; } = "https://your-site.xyz";
}
public class OAuth2Data
{
[JsonProperty("OverrideUrl")] public string OverrideUrl { get; set; } = "https://only-for-development.cases";
[JsonProperty("EnableOverrideUrl")] public bool EnableOverrideUrl { get; set; } = false;
[JsonProperty("Providers")]
public OAuth2ProviderData[] Providers { get; set; } = Array.Empty<OAuth2ProviderData>();
}
public class OAuth2ProviderData
{
[JsonProperty("Id")] public string Id { get; set; }
[JsonProperty("ClientId")] public string ClientId { get; set; }
[JsonProperty("ClientSecret")] public string ClientSecret { get; set; }
}
public class RatingData
{
[JsonProperty("Enabled")] public bool Enabled { get; set; } = false;
[JsonProperty("Url")] public string Url { get; set; } = "https://link-to-google-or-smth";
[JsonProperty("MinRating")] public int MinRating { get; set; } = 4;
[JsonProperty("DaysSince")] public int DaysSince { get; set; } = 5;
}
public class SecurityData
{
[JsonProperty("Token")] public string Token { get; set; } = Guid.NewGuid().ToString();
[JsonProperty("ReCaptcha")] public ReCaptchaData ReCaptcha { get; set; }
}
public class ReCaptchaData
{
[JsonProperty("Enable")] public bool Enable { get; set; } = false;
[JsonProperty("SiteKey")] public string SiteKey { get; set; } = "recaptcha site key here";
[JsonProperty("SecretKey")] public string SecretKey { get; set; } = "recaptcha secret here";
}
public class SentryData
{
[JsonProperty("Enable")] public bool Enable { get; set; } = false;
[JsonProperty("Dsn")] public string Dsn { get; set; } = "http://your-sentry-url-here";
}
public class SmartDeployData
{
[JsonProperty("Server")] public SmartDeployServerData Server { get; set; } = new();
}
public class SmartDeployServerData
{
[JsonProperty("EnableOverride")] public bool EnableOverride { get; set; } = false;
[JsonProperty("OverrideNode")] public long OverrideNode { get; set; } = 1;
}
public class StatisticsData
{
[JsonProperty("Enabled")] public bool Enabled { get; set; } = false;
[JsonProperty("Wait")] public long Wait { get; set; } = 15;
}
public class SubscriptionsData
{
[JsonProperty("SellPass")] public SellPassData SellPass { get; set; } = new();
}
public class SellPassData
{
[JsonProperty("Enable")] public bool Enable { get; set; } = false;
[JsonProperty("Url")] public string Url { get; set; } = "https://not-implemented-yet";
}
}

View File

@@ -52,14 +52,14 @@ public class DataContext : DbContext
if (!optionsBuilder.IsConfigured) if (!optionsBuilder.IsConfigured)
{ {
var config = ConfigService var config = ConfigService
.GetSection("Moonlight") .Get()
.GetSection("Database"); .Moonlight.Database;
var connectionString = $"host={config.GetValue<string>("Host")};" + var connectionString = $"host={config.Host};" +
$"port={config.GetValue<int>("Port")};" + $"port={config.Port};" +
$"database={config.GetValue<string>("Database")};" + $"database={config.Database};" +
$"uid={config.GetValue<string>("Username")};" + $"uid={config.Username};" +
$"pwd={config.GetValue<string>("Password")}"; $"pwd={config.Password}";
optionsBuilder.UseMySql( optionsBuilder.UseMySql(
connectionString, connectionString,

View File

@@ -0,0 +1,30 @@
using Microsoft.JSInterop;
namespace Moonlight.App.Extensions;
public static class JSRuntimeExtensions
{
public static async Task InvokeVoidSafeAsync(this IJSRuntime jsRuntime, string method, params object[] args)
{
try
{
await jsRuntime.InvokeVoidAsync(method, args);
}
catch (Exception)
{
// ignored
}
}
public static void InvokeVoidSafe(this IJSRuntime jsRuntime, string method, params object[] args)
{
try
{
jsRuntime.InvokeVoidAsync(method, args);
}
catch (Exception)
{
// ignored
}
}
}

View File

@@ -65,16 +65,14 @@ public class DatabaseCheckupService
var configService = new ConfigService(new StorageService()); var configService = new ConfigService(new StorageService());
var dateTimeService = new DateTimeService(); var dateTimeService = new DateTimeService();
var config = configService
.GetSection("Moonlight")
.GetSection("Database");
var connectionString = $"host={config.GetValue<string>("Host")};" + var config = configService.Get().Moonlight.Database;
$"port={config.GetValue<int>("Port")};" +
$"database={config.GetValue<string>("Database")};" + var connectionString = $"host={config.Host};" +
$"uid={config.GetValue<string>("Username")};" + $"port={config.Port};" +
$"pwd={config.GetValue<string>("Password")}"; $"database={config.Database};" +
$"uid={config.Username};" +
$"pwd={config.Password}";
string file = PathBuilder.File("storage", "backups", $"{dateTimeService.GetCurrentUnix()}-mysql.sql"); string file = PathBuilder.File("storage", "backups", $"{dateTimeService.GetCurrentUnix()}-mysql.sql");

View File

@@ -111,7 +111,7 @@ public class WingsFileAccess : FileAccess
request.AddParameter("name", "files"); request.AddParameter("name", "files");
request.AddParameter("filename", name); request.AddParameter("filename", name);
request.AddHeader("Content-Type", "multipart/form-data"); request.AddHeader("Content-Type", "multipart/form-data");
request.AddHeader("Origin", ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl")); request.AddHeader("Origin", ConfigService.Get().Moonlight.AppUrl);
request.AddFile("files", () => request.AddFile("files", () =>
{ {
return new StreamProgressHelper(dataStream) return new StreamProgressHelper(dataStream)

View File

@@ -20,7 +20,7 @@ public class WingsConsoleHelper
{ {
ServerRepository = serverRepository; ServerRepository = serverRepository;
AppUrl = configService.GetSection("Moonlight").GetValue<string>("AppUrl"); AppUrl = configService.Get().Moonlight.AppUrl;
} }
public async Task ConnectWings(WingsConsole console, Server server) public async Task ConnectWings(WingsConsole console, Server server)

View File

@@ -15,7 +15,7 @@ public class WingsJwtHelper
{ {
ConfigService = configService; ConfigService = configService;
AppUrl = ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl"); AppUrl = ConfigService.Get().Moonlight.AppUrl;
} }
public string Generate(string secret, Action<Dictionary<string, string>> claimsAction) public string Generate(string secret, Action<Dictionary<string, string>> claimsAction)

View File

@@ -30,14 +30,14 @@ public class DiscordBotController : Controller
ServerService = serverService; ServerService = serverService;
var config = configService var config = configService
.GetSection("Moonlight") .Get()
.GetSection("DiscordBotApi"); .Moonlight.DiscordBotApi;
Enable = config.GetValue<bool>("Enable"); Enable = config.Enable;
if (Enable) if (Enable)
{ {
Token = config.GetValue<string>("Token"); Token = config.Token;
} }
} }

View File

@@ -27,19 +27,39 @@ public class LogMigrator : ILogger
switch (logLevel) switch (logLevel)
{ {
case LogLevel.Critical: case LogLevel.Critical:
Logger.Fatal($"[{Name}] {formatter(state, exception)}"); Logger.Fatal(formatter(state, exception));
if(exception != null)
Logger.Fatal(exception);
break; break;
case LogLevel.Warning: case LogLevel.Warning:
Logger.Warn($"[{Name}] {formatter(state, exception)}"); Logger.Warn(formatter(state, exception));
if(exception != null)
Logger.Warn(exception);
break; break;
case LogLevel.Debug: case LogLevel.Debug:
Logger.Debug($"[{Name}] {formatter(state, exception)}"); Logger.Debug(formatter(state, exception));
if(exception != null)
Logger.Debug(exception);
break; break;
case LogLevel.Error: case LogLevel.Error:
Logger.Error($"[{Name}] {formatter(state, exception)}"); Logger.Error(formatter(state, exception));
if(exception != null)
Logger.Error(exception);
break; break;
case LogLevel.Information: case LogLevel.Information:
Logger.Info($"[{Name}] {formatter(state, exception)}"); Logger.Info(formatter(state, exception));
if(exception != null)
Logger.Info(exception);
break; break;
} }
} }

View File

@@ -0,0 +1,71 @@
using Moonlight.App.Helpers;
using Sentry;
using Sentry.Extensibility;
namespace Moonlight.App.LogMigrator;
public class SentryDiagnosticsLogger : IDiagnosticLogger
{
private readonly SentryLevel Level;
public SentryDiagnosticsLogger(SentryLevel level)
{
Level = level;
}
public bool IsEnabled(SentryLevel level)
{
if ((int)level >= (int)Level)
{
return true;
}
return false;
}
public void Log(SentryLevel logLevel, string message, Exception? exception = null, params object?[] args)
{
switch (logLevel)
{
case SentryLevel.Debug:
Logger.Debug(string.Format(message, args));
if(exception != null)
Logger.Debug(exception);
break;
case SentryLevel.Info:
Logger.Info(string.Format(message, args));
if(exception != null)
Logger.Info(exception);
break;
case SentryLevel.Warning:
Logger.Warn(string.Format(message, args));
if(exception != null)
Logger.Warn(exception);
break;
case SentryLevel.Error:
Logger.Error(string.Format(message, args));
if(exception != null)
Logger.Error(exception);
break;
case SentryLevel.Fatal:
Logger.Fatal(string.Format(message, args));
if(exception != null)
Logger.Fatal(exception);
break;
}
}
}

View File

@@ -42,16 +42,16 @@ public class CleanupService
StartedAt = DateTimeService.GetCurrent(); StartedAt = DateTimeService.GetCurrent();
CompletedAt = DateTimeService.GetCurrent(); CompletedAt = DateTimeService.GetCurrent();
IsRunning = false; IsRunning = false;
var config = ConfigService.GetSection("Moonlight").GetSection("Cleanup");
if (!config.GetValue<bool>("Enable") || ConfigService.DebugMode) var config = ConfigService.Get().Moonlight.Cleanup;
if (!config.Enable || ConfigService.DebugMode)
{ {
Logger.Info("Disabling cleanup service"); Logger.Info("Disabling cleanup service");
return; return;
} }
Timer = new(TimeSpan.FromMinutes(config.GetValue<int>("Wait"))); Timer = new(TimeSpan.FromMinutes(config.Wait));
Task.Run(Run); Task.Run(Run);
} }
@@ -63,12 +63,12 @@ public class CleanupService
IsRunning = true; IsRunning = true;
using var scope = ServiceScopeFactory.CreateScope(); using var scope = ServiceScopeFactory.CreateScope();
var config = ConfigService.GetSection("Moonlight").GetSection("Cleanup"); var config = ConfigService.Get().Moonlight.Cleanup;
var maxCpu = config.GetValue<int>("Cpu"); var maxCpu = config.Cpu;
var minMemory = config.GetValue<int>("Memory"); var minMemory = config.Memory;
var maxUptime = config.GetValue<int>("Uptime"); var maxUptime = config.Uptime;
var minUptime = config.GetValue<int>("MinUptime"); var minUptime = config.MinUptime;
var nodeRepository = scope.ServiceProvider.GetRequiredService<NodeRepository>(); var nodeRepository = scope.ServiceProvider.GetRequiredService<NodeRepository>();
var nodeService = scope.ServiceProvider.GetRequiredService<NodeService>(); var nodeService = scope.ServiceProvider.GetRequiredService<NodeService>();

View File

@@ -22,14 +22,14 @@ public class DiscordNotificationService
Event = eventSystem; Event = eventSystem;
ResourceService = resourceService; ResourceService = resourceService;
var config = configService.GetSection("Moonlight").GetSection("DiscordNotifications"); var config = configService.Get().Moonlight.DiscordNotifications;
if (config.GetValue<bool>("Enable")) if (config.Enable)
{ {
Logger.Info("Discord notifications enabled"); Logger.Info("Discord notifications enabled");
Client = new(config.GetValue<string>("WebHook")); Client = new(config.WebHook);
AppUrl = configService.GetSection("Moonlight").GetValue<string>("AppUrl"); AppUrl = configService.Get().Moonlight.AppUrl;
Event.On<User>("supportChat.new", this, OnNewSupportChat); Event.On<User>("supportChat.new", this, OnNewSupportChat);
Event.On<SupportChatMessage>("supportChat.message", this, OnSupportChatMessage); Event.On<SupportChatMessage>("supportChat.message", this, OnSupportChatMessage);

View File

@@ -1,15 +1,14 @@
using System.Text; using Moonlight.App.Configuration;
using Microsoft.Extensions.Primitives;
using Moonlight.App.Helpers; using Moonlight.App.Helpers;
using Moonlight.App.Services.Files; using Moonlight.App.Services.Files;
using Newtonsoft.Json;
namespace Moonlight.App.Services; namespace Moonlight.App.Services;
public class ConfigService : IConfiguration public class ConfigService
{ {
private readonly StorageService StorageService; private readonly StorageService StorageService;
private ConfigV1 Configuration;
private IConfiguration Configuration;
public bool DebugMode { get; private set; } = false; public bool DebugMode { get; private set; } = false;
public bool SqlDebugMode { get; private set; } = false; public bool SqlDebugMode { get; private set; } = false;
@@ -41,33 +40,22 @@ public class ConfigService : IConfiguration
public void Reload() public void Reload()
{ {
Configuration = new ConfigurationBuilder().AddJsonStream( var path = PathBuilder.File("storage", "configs", "config.json");
new MemoryStream(Encoding.ASCII.GetBytes(
File.ReadAllText( if (!File.Exists(path))
PathBuilder.File("storage", "configs", "config.json") {
) File.WriteAllText(path, "{}");
) }
)).Build();
Configuration = JsonConvert.DeserializeObject<ConfigV1>(
File.ReadAllText(path)
) ?? new ConfigV1();
File.WriteAllText(path, JsonConvert.SerializeObject(Configuration));
} }
public IEnumerable<IConfigurationSection> GetChildren() public ConfigV1 Get()
{ {
return Configuration.GetChildren(); return Configuration;
}
public IChangeToken GetReloadToken()
{
return Configuration.GetReloadToken();
}
public IConfigurationSection GetSection(string key)
{
return Configuration.GetSection(key);
}
public string this[string key]
{
get => Configuration[key];
set => Configuration[key] = value;
} }
} }

View File

@@ -29,7 +29,7 @@ public class ServerListCommand : BaseModule
{ {
embed = dcs.EmbedBuilderModule.StandardEmbed("Sorry ;( \n Please first create and/or link a Account to Discord! \n Press the Button to register/log in.", Color.Red, command.User); embed = dcs.EmbedBuilderModule.StandardEmbed("Sorry ;( \n Please first create and/or link a Account to Discord! \n Press the Button to register/log in.", Color.Red, command.User);
components = new ComponentBuilder(); components = new ComponentBuilder();
components.WithButton("Click Here", style: ButtonStyle.Link, url: ConfigService.GetSection("Moonlight").GetValue<String>("AppUrl")); components.WithButton("Click Here", style: ButtonStyle.Link, url: ConfigService.Get().Moonlight.AppUrl);
await command.RespondAsync(embed: embed.Build(), components: components.Build(), ephemeral: true); await command.RespondAsync(embed: embed.Build(), components: components.Build(), ephemeral: true);
return; return;
@@ -57,7 +57,7 @@ public class ServerListCommand : BaseModule
components.WithButton("Panel", components.WithButton("Panel",
emote: Emote.Parse("<a:Earth:1092814004113657927>"), emote: Emote.Parse("<a:Earth:1092814004113657927>"),
style: ButtonStyle.Link, style: ButtonStyle.Link,
url: $"{ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl")}"); url: $"{ConfigService.Get().Moonlight.AppUrl}");
if (servers.Count > 25) if (servers.Count > 25)
{ {

View File

@@ -44,10 +44,10 @@ public DiscordBotService(
ServiceScope = ServiceScopeFactory.CreateScope(); ServiceScope = ServiceScopeFactory.CreateScope();
var discordConfig = ConfigService var discordConfig = ConfigService
.GetSection("Moonlight") .Get()
.GetSection("DiscordBot"); .Moonlight.DiscordBot;
if (!discordConfig.GetValue<bool>("Enable")) if (!discordConfig.Enable)
return; return;
Client.Log += Log; Client.Log += Log;
@@ -67,7 +67,7 @@ public DiscordBotService(
await ActivityStatusModule.UpdateActivityStatusList(); await ActivityStatusModule.UpdateActivityStatusList();
await Client.LoginAsync(TokenType.Bot, discordConfig.GetValue<string>("Token")); await Client.LoginAsync(TokenType.Bot, discordConfig.Token);
await Client.StartAsync(); await Client.StartAsync();
await Task.Delay(-1); await Task.Delay(-1);

View File

@@ -87,8 +87,8 @@ public class EmbedBuilderModule : BaseModule
int[] randomNumbers = new int[] { 1, 3, 8, 11, 20 }; int[] randomNumbers = new int[] { 1, 3, 8, 11, 20 };
if (randomNumbers.Contains(random.Next(1, 24))) if (randomNumbers.Contains(random.Next(1, 24)))
return new EmbedAuthorBuilder().WithName(Client.CurrentUser.Username + " - The Rick version").WithUrl(ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl")).WithIconUrl("https://cdn.discordapp.com/attachments/750696464014901268/1092783310129860618/rick.gif"); return new EmbedAuthorBuilder().WithName(Client.CurrentUser.Username + " - The Rick version").WithUrl(ConfigService.Get().Moonlight.AppUrl).WithIconUrl("https://cdn.discordapp.com/attachments/750696464014901268/1092783310129860618/rick.gif");
return new EmbedAuthorBuilder().WithName(Client.CurrentUser.Username).WithUrl(ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl")).WithIconUrl(Client.CurrentUser.GetAvatarUrl()); return new EmbedAuthorBuilder().WithName(Client.CurrentUser.Username).WithUrl(ConfigService.Get().Moonlight.AppUrl).WithIconUrl(Client.CurrentUser.GetAvatarUrl());
} }
} }

View File

@@ -72,7 +72,7 @@ public class ServerListComponentHandlerModule : BaseModule
// stopping // stopping
// offline // offline
// installing // installing
if (!ConfigService.GetSection("Moonlight").GetSection("DiscordBot").GetValue<bool>("PowerActions") && costomId[1] is "Start" or "Restart" or "Stop" or "Kill" or "Update") if (!ConfigService.Get().Moonlight.DiscordBot.PowerActions && costomId[1] is "Start" or "Restart" or "Stop" or "Kill" or "Update")
{ {
embed = dcs.EmbedBuilderModule.StandardEmbed($"This feature is disabled for Security reasons! \n If you believe this is a error please contact the Administrators from this panel.", Color.Red, component.User); embed = dcs.EmbedBuilderModule.StandardEmbed($"This feature is disabled for Security reasons! \n If you believe this is a error please contact the Administrators from this panel.", Color.Red, component.User);
await component.RespondAsync(embed: embed.Build(), ephemeral: true); await component.RespondAsync(embed: embed.Build(), ephemeral: true);
@@ -80,7 +80,7 @@ public class ServerListComponentHandlerModule : BaseModule
return; return;
} }
if (!ConfigService.GetSection("Moonlight").GetSection("DiscordBot").GetValue<bool>("SendCommands") && costomId[1] is "SendCommand") if (!ConfigService.Get().Moonlight.DiscordBot.SendCommands && costomId[1] is "SendCommand")
{ {
embed = dcs.EmbedBuilderModule.StandardEmbed($"This feature is disabled for Security reasons! \n If you believe this is a error please contact the Administrators from this panel.", Color.Red, component.User); embed = dcs.EmbedBuilderModule.StandardEmbed($"This feature is disabled for Security reasons! \n If you believe this is a error please contact the Administrators from this panel.", Color.Red, component.User);
await component.RespondAsync(embed: embed.Build(), ephemeral: true); await component.RespondAsync(embed: embed.Build(), ephemeral: true);
@@ -302,7 +302,7 @@ public class ServerListComponentHandlerModule : BaseModule
{ {
embed = dcs.EmbedBuilderModule.StandardEmbed("Sorry ;( \n Please first create and/or link a Account to Discord! \n Press the Button to register/log in.", Color.Red, component.User); embed = dcs.EmbedBuilderModule.StandardEmbed("Sorry ;( \n Please first create and/or link a Account to Discord! \n Press the Button to register/log in.", Color.Red, component.User);
components = new ComponentBuilder(); components = new ComponentBuilder();
components.WithButton("Click Here", style: ButtonStyle.Link, url: ConfigService.GetSection("Moonlight").GetValue<String>("AppUrl")); components.WithButton("Click Here", style: ButtonStyle.Link, url: ConfigService.Get().Moonlight.AppUrl);
await component.RespondAsync(embed: embed.Build(), components: components.Build(), ephemeral: true); await component.RespondAsync(embed: embed.Build(), components: components.Build(), ephemeral: true);
return; return;
@@ -332,7 +332,7 @@ public class ServerListComponentHandlerModule : BaseModule
components.WithButton("Panel", components.WithButton("Panel",
emote: Emote.Parse("<a:Earth:1092814004113657927>"), emote: Emote.Parse("<a:Earth:1092814004113657927>"),
style: ButtonStyle.Link, style: ButtonStyle.Link,
url: $"{ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl")}"); url: $"{ConfigService.Get().Moonlight.AppUrl}");
components.WithButton("Previous-page", components.WithButton("Previous-page",
emote: Emote.Parse("<:ArrowLeft:1101547474180649030>"), emote: Emote.Parse("<:ArrowLeft:1101547474180649030>"),
@@ -378,7 +378,7 @@ public class ServerListComponentHandlerModule : BaseModule
var components = new ComponentBuilder(); var components = new ComponentBuilder();
if (ConfigService.GetSection("Moonlight").GetSection("DiscordBot").GetValue<bool>("PowerActions")) if (ConfigService.Get().Moonlight.DiscordBot.PowerActions)
{ {
components.WithButton("Start", style: ButtonStyle.Success, customId: $"Sm.Start.{server.Id}", disabled: false); components.WithButton("Start", style: ButtonStyle.Success, customId: $"Sm.Start.{server.Id}", disabled: false);
components.WithButton("Restart", style: ButtonStyle.Primary, customId: $"Sm.Restart.{server.Id}", disabled: false); components.WithButton("Restart", style: ButtonStyle.Primary, customId: $"Sm.Restart.{server.Id}", disabled: false);
@@ -389,14 +389,14 @@ public class ServerListComponentHandlerModule : BaseModule
components.WithButton("Way2Server", components.WithButton("Way2Server",
emote: Emote.Parse("<a:Earth:1092814004113657927>"), emote: Emote.Parse("<a:Earth:1092814004113657927>"),
style: ButtonStyle.Link, style: ButtonStyle.Link,
url: $"{ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl")}/server/{server.Uuid}"); url: $"{ConfigService.Get().Moonlight.AppUrl}/server/{server.Uuid}");
components.WithButton("Update", components.WithButton("Update",
emote: Emote.Parse("<:refresh:1101547898803605605>"), emote: Emote.Parse("<:refresh:1101547898803605605>"),
style: ButtonStyle.Secondary, style: ButtonStyle.Secondary,
customId: $"Sm.Update.{server.Id}"); customId: $"Sm.Update.{server.Id}");
if (ConfigService.GetSection("Moonlight").GetSection("DiscordBot").GetValue<bool>("SendCommands")) if (ConfigService.Get().Moonlight.DiscordBot.SendCommands)
{ {
components.WithButton("SendCommand", components.WithButton("SendCommand",
emote: Emote.Parse("<:Console:1101547358157819944>"), emote: Emote.Parse("<:Console:1101547358157819944>"),

View File

@@ -33,15 +33,15 @@ public class DomainService
SharedDomainRepository = sharedDomainRepository; SharedDomainRepository = sharedDomainRepository;
var config = configService var config = configService
.GetSection("Moonlight") .Get()
.GetSection("Domains"); .Moonlight.Domains;
AccountId = config.GetValue<string>("AccountId"); AccountId = config.AccountId;
Client = new( Client = new(
new ApiKeyAuthentication( new ApiKeyAuthentication(
config.GetValue<string>("Email"), config.Email,
config.GetValue<string>("Key") config.Key
) )
); );
} }

View File

@@ -8,7 +8,7 @@ public class ResourceService
public ResourceService(ConfigService configService) public ResourceService(ConfigService configService)
{ {
AppUrl = configService.GetSection("Moonlight").GetValue<string>("AppUrl"); AppUrl = configService.Get().Moonlight.AppUrl;
} }
public string Image(string name) public string Image(string name)

View File

@@ -15,6 +15,7 @@ public class StorageService
Directory.CreateDirectory(PathBuilder.Dir("storage", "configs")); Directory.CreateDirectory(PathBuilder.Dir("storage", "configs"));
Directory.CreateDirectory(PathBuilder.Dir("storage", "resources")); Directory.CreateDirectory(PathBuilder.Dir("storage", "resources"));
Directory.CreateDirectory(PathBuilder.Dir("storage", "backups")); Directory.CreateDirectory(PathBuilder.Dir("storage", "backups"));
Directory.CreateDirectory(PathBuilder.Dir("storage", "logs"));
if(IsEmpty(PathBuilder.Dir("storage", "resources"))) if(IsEmpty(PathBuilder.Dir("storage", "resources")))
{ {

View File

@@ -25,16 +25,15 @@ public class ReCaptchaService
ConfigService = configService; ConfigService = configService;
var recaptchaConfig = ConfigService var recaptchaConfig = ConfigService
.GetSection("Moonlight") .Get()
.GetSection("Security") .Moonlight.Security.ReCaptcha;
.GetSection("ReCaptcha");
Enable = recaptchaConfig.GetValue<bool>("Enable"); Enable = recaptchaConfig.Enable;
if (Enable) if (Enable)
{ {
SiteKey = recaptchaConfig.GetValue<string>("SiteKey"); SiteKey = recaptchaConfig.SiteKey;
SecretKey = recaptchaConfig.GetValue<string>("SecretKey"); SecretKey = recaptchaConfig.SecretKey;
} }
} }

View File

@@ -17,14 +17,14 @@ public class MailService
public MailService(ConfigService configService) public MailService(ConfigService configService)
{ {
var mailConfig = configService var mailConfig = configService
.GetSection("Moonlight") .Get()
.GetSection("Mail"); .Moonlight.Mail;
Server = mailConfig.GetValue<string>("Server"); Server = mailConfig.Server;
Password = mailConfig.GetValue<string>("Password"); Password = mailConfig.Password;
Email = mailConfig.GetValue<string>("Email"); Email = mailConfig.Email;
Port = mailConfig.GetValue<int>("Port"); Port = mailConfig.Port;
Ssl = mailConfig.GetValue<bool>("Ssl"); Ssl = mailConfig.Ssl;
} }
public async Task SendMail( public async Task SendMail(

View File

@@ -1,4 +1,5 @@
using Moonlight.App.Database.Entities; using Mappy.Net;
using Moonlight.App.Database.Entities;
using Moonlight.App.Exceptions; using Moonlight.App.Exceptions;
using Moonlight.App.Helpers; using Moonlight.App.Helpers;
using Moonlight.App.Models.Misc; using Moonlight.App.Models.Misc;
@@ -24,14 +25,17 @@ public class OAuth2Service
ConfigService = configService; ConfigService = configService;
ServiceScopeFactory = serviceScopeFactory; ServiceScopeFactory = serviceScopeFactory;
var config = ConfigService.GetSection("Moonlight").GetSection("OAuth2"); var config = ConfigService
.Get()
.Moonlight.OAuth2;
Configs = config.GetSection("Providers").Get<OAuth2ProviderConfig[]>() Configs = config.Providers
?? Array.Empty<OAuth2ProviderConfig>(); .Select(Mapper.Map<OAuth2ProviderConfig>)
.ToArray();
OverrideUrl = config.GetValue<string>("OverrideUrl"); OverrideUrl = config.OverrideUrl;
EnableOverrideUrl = config.GetValue<bool>("EnableOverrideUrl"); EnableOverrideUrl = config.EnableOverrideUrl;
AppUrl = configService.GetSection("Moonlight").GetValue<string>("AppUrl"); AppUrl = configService.Get().Moonlight.AppUrl;
// Register additional providers here // Register additional providers here
RegisterOAuth2<DiscordOAuth2Provider>("discord"); RegisterOAuth2<DiscordOAuth2Provider>("discord");

View File

@@ -25,10 +25,9 @@ public class OneTimeJwtService
var opt = new Dictionary<string, string>(); var opt = new Dictionary<string, string>();
options.Invoke(opt); options.Invoke(opt);
string secret = ConfigService var secret = ConfigService
.GetSection("Moonlight") .Get()
.GetSection("Security") .Moonlight.Security.Token;
.GetValue<string>("Token");
var id = StringHelper.GenerateString(16); var id = StringHelper.GenerateString(16);
@@ -55,10 +54,9 @@ public class OneTimeJwtService
public async Task<Dictionary<string, string>?> Validate(string token) public async Task<Dictionary<string, string>?> Validate(string token)
{ {
string secret = ConfigService var secret = ConfigService
.GetSection("Moonlight") .Get()
.GetSection("Security") .Moonlight.Security.Token;
.GetValue<string>("Token");
string json; string json;

View File

@@ -26,12 +26,12 @@ public class RatingService
Event = eventSystem; Event = eventSystem;
UserRepository = userRepository; UserRepository = userRepository;
var config = configService.GetSection("Moonlight").GetSection("Rating"); var config = configService.Get().Moonlight.Rating;
Enabled = config.GetValue<bool>("Enabled"); Enabled = config.Enabled;
Url = config.GetValue<string>("Url"); Url = config.Url;
MinRating = config.GetValue<int>("MinRating"); MinRating = config.MinRating;
DaysSince = config.GetValue<int>("DaysSince"); DaysSince = config.DaysSince;
} }
public async Task<bool> ShouldRate() public async Task<bool> ShouldRate()

View File

@@ -30,9 +30,8 @@ public class IdentityService
HttpContextAccessor = httpContextAccessor; HttpContextAccessor = httpContextAccessor;
Secret = configService Secret = configService
.GetSection("Moonlight") .Get()
.GetSection("Security") .Moonlight.Security.Token;
.GetValue<string>("Token");
} }
public async Task<User?> Get() public async Task<User?> Get()

View File

@@ -28,13 +28,12 @@ public class SmartDeployService
public async Task<Node?> GetNode() public async Task<Node?> GetNode()
{ {
var config = ConfigService var config = ConfigService
.GetSection("Moonlight") .Get()
.GetSection("SmartDeploy") .Moonlight.SmartDeploy.Server;
.GetSection("Server");
if (config.GetValue<bool>("EnableOverride")) if (config.EnableOverride)
{ {
var nodeId = config.GetValue<int>("OverrideNode"); var nodeId = config.OverrideNode;
return NodeRepository.Get().FirstOrDefault(x => x.Id == nodeId); return NodeRepository.Get().FirstOrDefault(x => x.Id == nodeId);
} }

View File

@@ -17,13 +17,13 @@ public class StatisticsCaptureService
DateTimeService = dateTimeService; DateTimeService = dateTimeService;
var config = configService var config = configService
.GetSection("Moonlight") .Get()
.GetSection("Statistics"); .Moonlight.Statistics;
if(!config.GetValue<bool>("Enabled")) if(!config.Enabled)
return; return;
var period = TimeSpan.FromMinutes(config.GetValue<int>("Wait")); var period = TimeSpan.FromMinutes(config.Wait);
Timer = new(period); Timer = new(period);
Logger.Info("Starting statistics system"); Logger.Info("Starting statistics system");

View File

@@ -38,9 +38,8 @@ public class UserService
DateTimeService = dateTimeService; DateTimeService = dateTimeService;
JwtSecret = configService JwtSecret = configService
.GetSection("Moonlight") .Get()
.GetSection("Security") .Moonlight.Security.Token;
.GetValue<string>("Token");
} }
public async Task<string> Register(string email, string password, string firstname, string lastname) public async Task<string> Register(string email, string password, string firstname, string lastname)

View File

@@ -46,7 +46,10 @@
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" /> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" />
<PackageReference Include="QRCoder" Version="1.4.3" /> <PackageReference Include="QRCoder" Version="1.4.3" />
<PackageReference Include="RestSharp" Version="109.0.0-preview.1" /> <PackageReference Include="RestSharp" Version="109.0.0-preview.1" />
<PackageReference Include="Sentry.AspNetCore" Version="3.33.1" />
<PackageReference Include="Sentry.Serilog" Version="3.33.1" />
<PackageReference Include="Serilog" Version="3.0.0" /> <PackageReference Include="Serilog" Version="3.0.0" />
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.1-dev-00910" /> <PackageReference Include="Serilog.Sinks.Console" Version="4.1.1-dev-00910" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.1-dev-00947" /> <PackageReference Include="Serilog.Sinks.File" Version="5.0.1-dev-00947" />
<PackageReference Include="SSH.NET" Version="2020.0.2" /> <PackageReference Include="SSH.NET" Version="2020.0.2" />
@@ -100,4 +103,12 @@
<AdditionalFiles Include="Shared\Views\Server\Settings\ServerResetSetting.razor" /> <AdditionalFiles Include="Shared\Views\Server\Settings\ServerResetSetting.razor" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Update="storage\configs\config.json.bak">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
</Project> </Project>

View File

@@ -11,12 +11,12 @@
@{ @{
var headerConfig = ConfigService var headerConfig = ConfigService
.GetSection("Moonlight") .Get()
.GetSection("Html") .Moonlight.Html.Headers;
.GetSection("Headers");
var moonlightConfig = ConfigService var moonlightConfig = ConfigService
.GetSection("Moonlight"); .Get()
.Moonlight;
} }
<!DOCTYPE html> <!DOCTYPE html>
@@ -26,16 +26,16 @@
<meta property="og:locale" content="de_DE"/> <meta property="og:locale" content="de_DE"/>
<meta property="og:type" content="article"/> <meta property="og:type" content="article"/>
<meta content="@(headerConfig.GetValue<string>("Title"))" property="og:title"/> <meta content="@(headerConfig.Title)" property="og:title"/>
<meta content="@(headerConfig.GetValue<string>("Description"))" property="og:description"/> <meta content="@(headerConfig.Description)" property="og:description"/>
<meta content="@(moonlightConfig.GetValue<string>("AppUrl"))" property="og:url"/> <meta content="@(moonlightConfig.AppUrl)" property="og:url"/>
<meta content="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logolong.png" property="og:image"/> <meta content="@(moonlightConfig.AppUrl)/api/moonlight/resources/images/logolong.png" property="og:image"/>
<meta content="@(headerConfig.GetValue<string>("Color"))" data-react-helmet="true" name="theme-color"/> <meta content="@(headerConfig.Color)" data-react-helmet="true" name="theme-color"/>
<meta content="@(headerConfig.GetValue<string>("Description"))" name="description"/> <meta content="@(headerConfig.Description)" name="description"/>
<meta content="@(headerConfig.GetValue<string>("Keywords"))" name="keywords"/> <meta content="@(headerConfig.Keywords)" name="keywords"/>
<link rel="shortcut icon" href="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logo.svg"/> <link rel="shortcut icon" href="@(moonlightConfig.AppUrl)/api/moonlight/resources/images/logo.svg"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Poppins:300,400,500,600,700"/> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Poppins:300,400,500,600,700"/>
@@ -75,7 +75,7 @@
<div id="flashbang" class="flashbanglight"></div> <div id="flashbang" class="flashbanglight"></div>
<div class="app-page-loader flex-column"> <div class="app-page-loader flex-column">
<img alt="Logo" src="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logo.svg" class="h-25px"/> <img alt="Logo" src="@(moonlightConfig.AppUrl)/api/moonlight/resources/images/logo.svg" class="h-25px"/>
@{ @{
string loadingMessage; string loadingMessage;

View File

@@ -29,7 +29,9 @@ using Moonlight.App.Services.Notifications;
using Moonlight.App.Services.Sessions; using Moonlight.App.Services.Sessions;
using Moonlight.App.Services.Statistics; using Moonlight.App.Services.Statistics;
using Moonlight.App.Services.SupportChat; using Moonlight.App.Services.SupportChat;
using Sentry;
using Serilog; using Serilog;
using Serilog.Events;
using Serilog.Sinks.SystemConsole.Themes; using Serilog.Sinks.SystemConsole.Themes;
namespace Moonlight namespace Moonlight
@@ -40,24 +42,65 @@ namespace Moonlight
{ {
// This will also copy all default config files // This will also copy all default config files
var configService = new ConfigService(new StorageService()); var configService = new ConfigService(new StorageService());
var shouldUseSentry = configService
.Get()
.Moonlight.Sentry.Enable;
if (configService.DebugMode) if (configService.DebugMode)
{ {
Log.Logger = new LoggerConfiguration() if (shouldUseSentry)
.MinimumLevel.Verbose() {
.Enrich.FromLogContext() Log.Logger = new LoggerConfiguration()
.WriteTo.Console( .MinimumLevel.Verbose()
outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}") .Enrich.FromLogContext()
.CreateLogger(); .WriteTo.Console(
outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}")
.WriteTo.File(PathBuilder.File("storage", "logs", $"{DateTime.UtcNow:yyyy-MM-dd}.log"))
.WriteTo.Sentry(options =>
{
options.MinimumBreadcrumbLevel = LogEventLevel.Debug;
options.MinimumEventLevel = LogEventLevel.Warning;
})
.CreateLogger();
}
else
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.FromLogContext()
.WriteTo.Console(
outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}")
.WriteTo.File(PathBuilder.File("storage", "logs", $"{DateTime.UtcNow:yyyy-MM-dd}.log"))
.CreateLogger();
}
} }
else else
{ {
Log.Logger = new LoggerConfiguration() if (shouldUseSentry)
.MinimumLevel.Information() {
.Enrich.FromLogContext() Log.Logger = new LoggerConfiguration()
.WriteTo.Console( .MinimumLevel.Information()
outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}") .Enrich.FromLogContext()
.CreateLogger(); .WriteTo.Console(
outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}")
.WriteTo.Sentry(options =>
{
options.MinimumBreadcrumbLevel = LogEventLevel.Information;
options.MinimumEventLevel = LogEventLevel.Warning;
})
.WriteTo.File(PathBuilder.File("storage", "logs", $"{DateTime.UtcNow:yyyy-MM-dd}.log"))
.CreateLogger();
}
else
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.Console(
outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}")
.WriteTo.File(PathBuilder.File("storage", "logs", $"{DateTime.UtcNow:yyyy-MM-dd}.log"))
.CreateLogger();
}
} }
Logger.Info($"Working dir: {Directory.GetCurrentDirectory()}"); Logger.Info($"Working dir: {Directory.GetCurrentDirectory()}");
@@ -66,13 +109,13 @@ namespace Moonlight
var databaseCheckupService = new DatabaseCheckupService(configService); var databaseCheckupService = new DatabaseCheckupService(configService);
await databaseCheckupService.Perform(); await databaseCheckupService.Perform();
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
// Switch to logging.net injection // Switch to logging.net injection
// TODO: Enable in production // TODO: Enable in production
//builder.Logging.ClearProviders(); builder.Logging.ClearProviders();
//builder.Logging.AddProvider(new LogMigratorProvider()); builder.Logging.AddProvider(new LogMigratorProvider());
// Add services to the container. // Add services to the container.
builder.Services.AddRazorPages(); builder.Services.AddRazorPages();
@@ -88,6 +131,23 @@ namespace Moonlight
.AddCheck<DatabaseHealthCheck>("Database") .AddCheck<DatabaseHealthCheck>("Database")
.AddCheck<NodeHealthCheck>("Nodes") .AddCheck<NodeHealthCheck>("Nodes")
.AddCheck<DaemonHealthCheck>("Daemons"); .AddCheck<DaemonHealthCheck>("Daemons");
// Sentry
if (shouldUseSentry)
{
builder.WebHost.UseSentry(options =>
{
options.Dsn = configService
.Get()
.Moonlight.Sentry.Dsn;
options.Debug = configService.DebugMode;
options.DiagnosticLevel = SentryLevel.Warning;
options.TracesSampleRate = 1.0;
options.DiagnosticLogger = new SentryDiagnosticsLogger(SentryLevel.Warning);
});
}
// Databases // Databases
builder.Services.AddDbContext<DataContext>(); builder.Services.AddDbContext<DataContext>();
@@ -203,6 +263,12 @@ namespace Moonlight
app.UseHsts(); app.UseHsts();
} }
// Sentry
if (shouldUseSentry)
{
app.UseSentryTracing();
}
app.UseStaticFiles(); app.UseStaticFiles();
app.UseRouting(); app.UseRouting();
app.UseWebSockets(); app.UseWebSockets();

View File

@@ -1,16 +1,12 @@
@using Moonlight.App.Services @using Moonlight.App.Services
@using Moonlight.App.Services.Files
@inject ConfigService ConfigService @inject ResourceService ResourceService
@{
var moonlightConfig = ConfigService
.GetSection("Moonlight");
}
<div class="card card-flush w-lg-650px py-5"> <div class="card card-flush w-lg-650px py-5">
<div class="card-body py-15 py-lg-20"> <div class="card-body py-15 py-lg-20">
<div class="mb-14"> <div class="mb-14">
<img alt="Logo" src="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logolong.png" class="h-40px"> <img alt="Logo" src="@(ResourceService.Image("logolong.png"))" class="h-40px">
</div> </div>
<h1 class="fw-bolder text-gray-900 mb-5"><TL>Your account is banned from moonlight</TL></h1> <h1 class="fw-bolder text-gray-900 mb-5"><TL>Your account is banned from moonlight</TL></h1>
<div class="fw-semibold fs-6 text-gray-500 mb-8"> <div class="fw-semibold fs-6 text-gray-500 mb-8">

View File

@@ -1,16 +1,12 @@
@using Moonlight.App.Services @using Moonlight.App.Services
@using Moonlight.App.Services.Files
@inject ConfigService ConfigService @inject ResourceService ResourceService
@{
var moonlightConfig = ConfigService
.GetSection("Moonlight");
}
<div class="card card-flush w-lg-650px py-5"> <div class="card card-flush w-lg-650px py-5">
<div class="card-body py-15 py-lg-20"> <div class="card-body py-15 py-lg-20">
<div class="mb-14"> <div class="mb-14">
<img alt="Logo" src="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logolong.png" class="h-40px"> <img alt="Logo" src="@(ResourceService.Image("logolong.png"))" class="h-40px">
</div> </div>
<h1 class="fw-bolder text-gray-900 mb-5"><TL>Your moonlight account is disabled</TL></h1> <h1 class="fw-bolder text-gray-900 mb-5"><TL>Your moonlight account is disabled</TL></h1>
<div class="fw-semibold fs-6 text-gray-500 mb-8"> <div class="fw-semibold fs-6 text-gray-500 mb-8">

View File

@@ -10,8 +10,8 @@
</a> </a>
</li> </li>
<li class="nav-item mt-2"> <li class="nav-item mt-2">
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 1 ? "active" : "")" href="/admin/system/logs"> <a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 1 ? "active" : "")" href="/admin/system/sentry">
<TL>Logs</TL> <TL>Sentry</TL>
</a> </a>
</li> </li>
<li class="nav-item mt-2"> <li class="nav-item mt-2">

View File

@@ -4,31 +4,31 @@
@{ @{
var marketingConfig = ConfigService var marketingConfig = ConfigService
.GetSection("Moonlight") .Get()
.GetSection("Marketing"); .Moonlight.Marketing;
} }
<div id="kt_app_footer" class="app-footer"> <div id="kt_app_footer" class="app-footer">
<div class="app-container container-fluid d-flex flex-column flex-md-row flex-center flex-md-stack py-3"> <div class="app-container container-fluid d-flex flex-column flex-md-row flex-center flex-md-stack py-3">
<div class="text-dark order-2 order-md-1"> <div class="text-dark order-2 order-md-1">
<span class="text-muted fw-semibold me-1">2022 - @DateTime.Now.Year©</span> <span class="text-muted fw-semibold me-1">2022 - @DateTime.Now.Year©</span>
<a href="@(marketingConfig.GetValue<string>("Website"))" target="_blank" class="text-gray-800 text-hover-primary"> <a href="@(marketingConfig.Website)" target="_blank" class="text-gray-800 text-hover-primary">
@(marketingConfig.GetValue<string>("BrandName")) @(marketingConfig.BrandName)
</a> </a>
</div> </div>
<ul class="menu menu-gray-600 menu-hover-primary fw-semibold order-1"> <ul class="menu menu-gray-600 menu-hover-primary fw-semibold order-1">
<li class="menu-item"> <li class="menu-item">
<a href="@(marketingConfig.GetValue<string>("About"))" target="_blank" class="menu-link px-2"> <a href="@(marketingConfig.About)" target="_blank" class="menu-link px-2">
<TL>About us</TL> <TL>About us</TL>
</a> </a>
</li> </li>
<li class="menu-item"> <li class="menu-item">
<a href="@(marketingConfig.GetValue<string>("Imprint"))" target="_blank" class="menu-link px-2"> <a href="@(marketingConfig.Imprint)" target="_blank" class="menu-link px-2">
<TL>Imprint</TL> <TL>Imprint</TL>
</a> </a>
</li> </li>
<li class="menu-item"> <li class="menu-item">
<a href="@(marketingConfig.GetValue<string>("Privacy"))" target="_blank" class="menu-link px-2"> <a href="@(marketingConfig.Privacy)" target="_blank" class="menu-link px-2">
<TL>Privacy</TL> <TL>Privacy</TL>
</a> </a>
</li> </li>

View File

@@ -1,10 +1,6 @@
@using Moonlight.App.Services @using Moonlight.App.Services.Files
@inject ConfigService ConfigService @inject ResourceService ResourceService
@{
var moonlightConfig = ConfigService.GetSection("Moonlight");
}
<div id="kt_app_header" class="app-header"> <div id="kt_app_header" class="app-header">
<div class="app-container container-fluid d-flex align-items-stretch justify-content-between"> <div class="app-container container-fluid d-flex align-items-stretch justify-content-between">
@@ -15,7 +11,7 @@
</div> </div>
<div class="d-flex align-items-center flex-grow-1 flex-lg-grow-0"> <div class="d-flex align-items-center flex-grow-1 flex-lg-grow-0">
<a href="/" class="d-lg-none"> <a href="/" class="d-lg-none">
<img alt="Logo" src="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logo.svg" class="h-30px"/> <img alt="Logo" src="@(ResourceService.Image("logo.svg"))" class="h-30px"/>
</a> </a>
</div> </div>
<div class="d-flex align-items-stretch justify-content-between flex-lg-grow-1" id="kt_app_header_wrapper"> <div class="d-flex align-items-stretch justify-content-between flex-lg-grow-1" id="kt_app_header_wrapper">

View File

@@ -1,32 +1,28 @@
@using Moonlight.App.Services.Sessions @using Moonlight.App.Services.Sessions
@using Moonlight.App.Database.Entities @using Moonlight.App.Database.Entities
@using Moonlight.App.Services @using Moonlight.App.Services
@using Moonlight.App.Services.Files
@inject IdentityService IdentityService @inject IdentityService IdentityService
@inject ConfigService ConfigService @inject ResourceService ResourceService
@inject IJSRuntime JsRuntime @inject IJSRuntime JsRuntime
@{
var moonlightConfig = ConfigService
.GetSection("Moonlight");
}
<div id="kt_app_sidebar" class="app-sidebar flex-column" data-kt-drawer="true" data-kt-drawer-name="app-sidebar" data-kt-drawer-activate="{default: true, lg: false}" data-kt-drawer-overlay="true" data-kt-drawer-width="225px" data-kt-drawer-direction="start" data-kt-drawer-toggle="#kt_app_sidebar_mobile_toggle"> <div id="kt_app_sidebar" class="app-sidebar flex-column" data-kt-drawer="true" data-kt-drawer-name="app-sidebar" data-kt-drawer-activate="{default: true, lg: false}" data-kt-drawer-overlay="true" data-kt-drawer-width="225px" data-kt-drawer-direction="start" data-kt-drawer-toggle="#kt_app_sidebar_mobile_toggle">
<div class="app-sidebar-logo px-6" id="kt_app_sidebar_logo"> <div class="app-sidebar-logo px-6" id="kt_app_sidebar_logo">
<a href="@(User != null ? "/" : "/login")"> <a href="@(User != null ? "/" : "/login")">
@if (sidebar == "dark-sidebar") @if (sidebar == "dark-sidebar")
{ {
<img alt="Logo" src="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logolong.png" class="h-45px app-sidebar-logo-default"/> <img alt="Logo" src="@(ResourceService.Image("logolong.png"))" class="h-45px app-sidebar-logo-default"/>
} }
else else
{ {
if (sidebar == "light-sidebar") if (sidebar == "light-sidebar")
{ {
<img alt="Logo" src="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logo.svg" class="theme-light-show h-20px app-sidebar-logo-default"/> <img alt="Logo" src="@(ResourceService.Image("logo.svg"))" class="theme-light-show h-20px app-sidebar-logo-default"/>
<img alt="Logo" src="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logo.svg" class="theme-dark-show h-20px app-sidebar-logo-default"/> <img alt="Logo" src="@(ResourceService.Image("logo.svg"))" class="theme-dark-show h-20px app-sidebar-logo-default"/>
} }
} }
<img alt="Logo" src="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logo.svg" class="h-20px app-sidebar-logo-minimize"/> <img alt="Logo" src="@(ResourceService.Image("logo.svg"))" class="h-20px app-sidebar-logo-minimize"/>
</a> </a>
<div id="kt_app_sidebar_toggle" class="app-sidebar-toggle btn btn-icon btn-shadow btn-sm btn-color-muted btn-active-color-primary body-bg h-30px w-30px position-absolute top-50 start-100 translate-middle rotate" data-kt-toggle="true" data-kt-toggle-state="active" data-kt-toggle-target="body" data-kt-toggle-name="app-sidebar-minimize"> <div id="kt_app_sidebar_toggle" class="app-sidebar-toggle btn btn-icon btn-shadow btn-sm btn-color-muted btn-active-color-primary body-bg h-30px w-30px position-absolute top-50 start-100 translate-middle rotate" data-kt-toggle="true" data-kt-toggle-state="active" data-kt-toggle-target="body" data-kt-toggle-name="app-sidebar-minimize">

View File

@@ -1,20 +0,0 @@
@using Moonlight.App.Services
@inject ConfigService ConfigService
@{
var setupComplete = ConfigService
.GetSection("Moonlight")
.GetValue<bool>("SetupComplete");
}
@if (!setupComplete)
{
@ChildContent
}
@code
{
[Parameter]
public RenderFragment ChildContent { get; set; }
}

View File

@@ -1,20 +0,0 @@
@using Moonlight.App.Services
@inject ConfigService ConfigService
@{
var setupComplete = ConfigService
.GetSection("Moonlight")
.GetValue<bool>("SetupComplete");
}
@if (setupComplete)
{
@ChildContent
}
@code
{
[Parameter]
public RenderFragment ChildContent { get; set; }
}

View File

@@ -1,4 +1,5 @@
@inherits LayoutComponentBase @using Moonlight.App.Extensions
@inherits LayoutComponentBase
@inject IJSRuntime JS @inject IJSRuntime JS
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@@ -14,23 +15,23 @@ window.emptyBody = function(){
{ {
protected override void OnAfterRender(bool firstRender) protected override void OnAfterRender(bool firstRender)
{ {
JS.InvokeVoidAsync("KTThemeMode.init"); JS.InvokeVoidSafe("KTThemeMode.init");
JS.InvokeVoidAsync("emptyBody"); JS.InvokeVoidSafe("emptyBody");
if (firstRender) if (firstRender)
{ {
JS.InvokeVoidAsync("scrollTo", 0, 0); JS.InvokeVoidSafe("scrollTo", 0, 0);
JS.InvokeVoidAsync("KTDialer.init"); JS.InvokeVoidSafe("KTDialer.init");
JS.InvokeVoidAsync("KTDrawer.init"); JS.InvokeVoidSafe("KTDrawer.init");
JS.InvokeVoidAsync("KTMenu.init"); JS.InvokeVoidSafe("KTMenu.init");
JS.InvokeVoidAsync("KTImageInput.init"); JS.InvokeVoidSafe("KTImageInput.init");
JS.InvokeVoidAsync("KTPasswordMeter.init"); JS.InvokeVoidSafe("KTPasswordMeter.init");
JS.InvokeVoidAsync("KTScroll.init"); JS.InvokeVoidSafe("KTScroll.init");
JS.InvokeVoidAsync("KTScrolltop.init"); JS.InvokeVoidSafe("KTScrolltop.init");
JS.InvokeVoidAsync("KTSticky.init"); JS.InvokeVoidSafe("KTSticky.init");
JS.InvokeVoidAsync("KTSwapper.init"); JS.InvokeVoidSafe("KTSwapper.init");
JS.InvokeVoidAsync("KTToggle.init"); JS.InvokeVoidSafe("KTToggle.init");
JS.InvokeVoidAsync("KTMenu.updateByLinkAttribute", $"/{NavigationManager.ToBaseRelativePath(NavigationManager.Uri)}"); JS.InvokeVoidSafe("KTMenu.updateByLinkAttribute", $"/{NavigationManager.ToBaseRelativePath(NavigationManager.Uri)}");
} }
JS.InvokeVoidAsync("KTLayoutSearch.init"); JS.InvokeVoidAsync("KTLayoutSearch.init");
@@ -45,30 +46,18 @@ window.emptyBody = function(){
private async void OnLocationChanged(object sender, LocationChangedEventArgs args) private async void OnLocationChanged(object sender, LocationChangedEventArgs args)
{ {
await InvokeJsSave("scrollTo", 0, 0); await JS.InvokeVoidSafeAsync("scrollTo", 0, 0);
await InvokeJsSave("KTDrawer.createInstances"); await JS.InvokeVoidSafeAsync("KTDrawer.createInstances");
await InvokeJsSave("KTMenu.createInstances"); await JS.InvokeVoidSafeAsync("KTMenu.createInstances");
await InvokeJsSave("KTImageInput.createInstances"); await JS.InvokeVoidSafeAsync("KTImageInput.createInstances");
await InvokeJsSave("KTPasswordMeter.createInstances"); await JS.InvokeVoidSafeAsync("KTPasswordMeter.createInstances");
await InvokeJsSave("KTScroll.createInstances"); await JS.InvokeVoidSafeAsync("KTScroll.createInstances");
await InvokeJsSave("KTScrolltop.createInstances"); await JS.InvokeVoidSafeAsync("KTScrolltop.createInstances");
await InvokeJsSave("KTSticky.createInstances"); await JS.InvokeVoidSafeAsync("KTSticky.createInstances");
await InvokeJsSave("KTSwapper.createInstances"); await JS.InvokeVoidSafeAsync("KTSwapper.createInstances");
await InvokeJsSave("KTToggle.createInstances"); await JS.InvokeVoidSafeAsync("KTToggle.createInstances");
await InvokeJsSave("KTMenu.updateByLinkAttribute", $"/{NavigationManager.ToBaseRelativePath(args.Location)}"); await JS.InvokeVoidSafeAsync("KTMenu.updateByLinkAttribute", $"/{NavigationManager.ToBaseRelativePath(args.Location)}");
await InvokeJsSave("KTAppSidebar.init"); await JS.InvokeVoidSafeAsync("KTAppSidebar.init");
}
private async Task InvokeJsSave(string method, params object[] args)
{
try
{
await JS.InvokeVoidAsync(method, args);
}
catch (Exception)
{
// ignored
}
} }
public void Dispose() public void Dispose()

View File

@@ -151,8 +151,8 @@
await lazyLoader.SetText("Loading health check data"); await lazyLoader.SetText("Loading health check data");
var appUrl = ConfigService var appUrl = ConfigService
.GetSection("Moonlight") .Get()
.GetValue<string>("AppUrl"); .Moonlight.AppUrl;
try try
{ {

View File

@@ -4,12 +4,10 @@
@using Moonlight.App.Services @using Moonlight.App.Services
@inject NodeRepository NodeRepository @inject NodeRepository NodeRepository
@inject SmartTranslateService SmartTranslateService
@inject ConfigService ConfigService @inject ConfigService ConfigService
@inject NavigationManager NavigationManager
@{ @{
var appUrl = ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl"); var appUrl = ConfigService.Get().Moonlight.AppUrl;
} }
<OnlyAdmin> <OnlyAdmin>

View File

@@ -1,20 +0,0 @@
@page "/admin/system/logs"
@using BlazorTable
@using Moonlight.App.Models.Misc
@using Moonlight.App.Services
@using Moonlight.Shared.Components.Navigations
@inject SmartTranslateService SmartTranslateService
<OnlyAdmin>
<AdminSystemNavigation Index="1"/>
</OnlyAdmin>
@code
{
private Task Load(LazyLoader arg)
{
return Task.CompletedTask;
}
}

View File

@@ -0,0 +1,40 @@
@page "/admin/system/sentry"
@using Moonlight.Shared.Components.Navigations
@using global::Sentry
<OnlyAdmin>
<AdminSystemNavigation Index="1"/>
<div class="row">
<div class="col-xxl-6 my-3">
<div class="card">
<div class="card-header">
<span class="card-title">
<TL>Status</TL>
</span>
</div>
<div class="card-body">
<span class="fs-5">
@if (SentrySdk.IsEnabled)
{
<TL>Sentry is enabled</TL>
}
else
{
<TL>Sentry is disabled</TL>
}
</span>
</div>
</div>
</div>
</div>
</OnlyAdmin>
@code
{
private Task Load(LazyLoader arg)
{
return Task.CompletedTask;
}
}

View File

@@ -24,12 +24,11 @@
@if (Subscription == null) @if (Subscription == null)
{ {
var config = ConfigService var config = ConfigService
.GetSection("Moonlight") .Get()
.GetSection("Subscriptions") .Moonlight.Subscriptions.SellPass;
.GetSection("Sellpass");
var enableSellpass = config.GetValue<bool>("Enable"); var enableSellpass = config.Enable;
var url = config.GetValue<string>("Url"); var url = config.Url;
<h3 class="mb-2"> <h3 class="mb-2">
<div class="input-group mb-3"> <div class="input-group mb-3">

View File

@@ -14,171 +14,174 @@
@inject IJSRuntime JsRuntime @inject IJSRuntime JsRuntime
<LazyLoader @ref="LazyLoader" Load="Load"> <LazyLoader @ref="LazyLoader" Load="Load">
<div class="mx-auto"> <div class="mx-auto">
<div class="card card-body"> <div class="card card-body">
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<span class="badge badge-primary badge-lg px-5 me-4">Beta</span> <span class="badge badge-primary badge-lg px-5 me-4">Beta</span>
@if (EditMode) @if (EditMode)
{ {
<div> <div>
<WButton Text="@(SmartTranslateService.Translate("New group"))" <WButton Text="@(SmartTranslateService.Translate("New group"))"
WorkingText="" WorkingText=""
CssClasses="btn-primary me-3" CssClasses="btn-primary me-3"
OnClick="AddGroup"> OnClick="AddGroup">
</WButton> </WButton>
<WButton Text="@(SmartTranslateService.Translate("Finish editing layout"))" <WButton Text="@(SmartTranslateService.Translate("Finish editing layout"))"
CssClasses="btn-secondary" CssClasses="btn-secondary"
OnClick="async () => await SetEditMode(false)"> OnClick="async () => await SetEditMode(false)">
</WButton> </WButton>
</div>
}
else
{
<WButton Text="@(SmartTranslateService.Translate("Edit layout"))"
CssClasses="btn-secondary"
OnClick="async () => await SetEditMode(true)">
</WButton>
}
</div>
</div>
@foreach (var group in ServerGroups)
{
<div class="accordion my-3" id="serverListGroup@(group.GetHashCode())">
<div class="accordion-item">
<h2 class="accordion-header" id="serverListGroup-header@(group.GetHashCode())">
<button class="accordion-button fs-4 fw-semibold collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#serverListGroup-body@(group.GetHashCode())" aria-expanded="false" aria-controls="serverListGroup-body@(group.GetHashCode())">
<div class="d-flex justify-content-between">
<div>
@if (EditMode)
{
<input @bind="group.Name" class="form-control"/>
}
else
{
if (string.IsNullOrEmpty(group.Name))
{
<TL>Unsorted servers</TL>
}
else
{
<span>@(group.Name)</span>
}
}
</div>
<div>
@if (EditMode)
{
<WButton Text="@(SmartTranslateService.Translate("Remove group"))"
WorkingText=""
CssClasses="btn-danger"
OnClick="async () => await RemoveGroup(group)">
</WButton>
}
</div>
</div> </div>
</button> }
</h2> else
<div id="serverListGroup-body@(group.GetHashCode())" class="accordion-collapse collapse" aria-labelledby="serverListGroup-header@(group.GetHashCode())" data-bs-parent="#serverListGroup"> {
<div class="accordion-body"> <WButton Text="@(SmartTranslateService.Translate("Edit layout"))"
<div class="row min-h-200px draggable-zone" ml-server-group="@(group.Name)"> CssClasses="btn-secondary"
@foreach (var id in group.Servers) OnClick="async () => await SetEditMode(true)">
{ </WButton>
var server = AllServers.First(x => x.Id.ToString() == id); }
</div>
<div class="col-12 col-md-3 p-3 draggable" ml-server-id="@(server.Id)"> </div>
@if (EditMode) @foreach (var group in ServerGroups)
{
<div class="accordion my-3" id="serverListGroup@(group.GetHashCode())">
<div class="accordion-item">
<h2 class="accordion-header" id="serverListGroup-header@(group.GetHashCode())">
<button class="accordion-button fs-4 fw-semibold collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#serverListGroup-body@(group.GetHashCode())" aria-expanded="false" aria-controls="serverListGroup-body@(group.GetHashCode())">
<div class="d-flex justify-content-between">
<div>
@if (EditMode)
{
<input @bind="group.Name" class="form-control"/>
}
else
{
if (string.IsNullOrEmpty(group.Name))
{
<TL>Unsorted servers</TL>
}
else
{
<span>@(group.Name)</span>
}
}
</div>
<div>
@if (EditMode)
{
<WButton Text="@(SmartTranslateService.Translate("Remove group"))"
WorkingText=""
CssClasses="btn-danger"
OnClick="async () => await RemoveGroup(group)">
</WButton>
}
</div>
</div>
</button>
</h2>
<div id="serverListGroup-body@(group.GetHashCode())" class="accordion-collapse collapse" aria-labelledby="serverListGroup-header@(group.GetHashCode())" data-bs-parent="#serverListGroup">
<div class="accordion-body">
<div class="row min-h-200px draggable-zone" ml-server-group="@(group.Name)">
@foreach (var id in group.Servers)
{ {
<div class="card bg-secondary"> var server = AllServers.FirstOrDefault(x => x.Id.ToString() == id);
<div class="card-header">
<div class="card-title"> if (server != null)
<span class="card-label">@(server.Name)</span> {
</div> <div class="col-12 col-md-3 p-3 draggable" ml-server-id="@(server.Id)">
<div class="card-toolbar"> @if (EditMode)
<a href="#" class="btn btn-icon btn-sm btn-hover-light-primary draggable-handle"> {
<i class="bx bx-md bx-move"></i> <div class="card bg-secondary">
<div class="card-header">
<div class="card-title">
<span class="card-label">@(server.Name)</span>
</div>
<div class="card-toolbar">
<a href="#" class="btn btn-icon btn-sm btn-hover-light-primary draggable-handle">
<i class="bx bx-md bx-move"></i>
</a>
</div>
</div>
<div class="card-body">
<TL>Hidden in edit mode</TL>
</div>
</div>
}
else
{
<a class="invisible-a" href="/server/@(server.Uuid)">
<div class="card bg-secondary">
<div class="card-header">
<div class="card-title">
<span class="card-label">@(server.Name)</span>
</div>
</div>
<div class="card-body">
<span class="card-text fs-6">
@(Math.Round(server.Memory / 1024D, 2)) GB / @(Math.Round(server.Disk / 1024D, 2)) GB / @(server.Node.Name) <span class="text-gray-700">- @(server.Image.Name)</span>
</span>
<div class="card-text my-1 fs-6 fw-bold @(User.StreamerMode ? "blur" : "")">
@(server.Node.Fqdn):@(server.MainAllocation.Port)
</div>
<div class="card-text fs-6">
@if (StatusCache.ContainsKey(server))
{
var status = StatusCache[server];
switch (status)
{
case "offline":
<span class="text-danger">
<TL>Offline</TL>
</span>
break;
case "stopping":
<span class="text-warning">
<TL>Stopping</TL>
</span>
break;
case "starting":
<span class="text-warning">
<TL>Starting</TL>
</span>
break;
case "running":
<span class="text-success">
<TL>Running</TL>
</span>
break;
case "failed":
<span class="text-gray-400">
<TL>Failed</TL>
</span>
break;
default:
<span class="text-danger">
<TL>Offline</TL>
</span>
break;
}
}
else
{
<span class="text-gray-400">
<TL>Loading</TL>
</span>
}
</div>
</div>
</div>
</a> </a>
</div> }
</div> </div>
<div class="card-body"> }
<TL>Hidden in edit mode</TL>
</div>
</div>
}
else
{
<a class="invisible-a" href="/server/@(server.Uuid)">
<div class="card bg-secondary">
<div class="card-header">
<div class="card-title">
<span class="card-label">@(server.Name)</span>
</div>
</div>
<div class="card-body">
<span class="card-text fs-6">
@(Math.Round(server.Memory / 1024D, 2)) GB / @(Math.Round(server.Disk / 1024D, 2)) GB / @(server.Node.Name) <span class="text-gray-700">- @(server.Image.Name)</span>
</span>
<div class="card-text my-1 fs-6 fw-bold @(User.StreamerMode ? "blur" : "")">
@(server.Node.Fqdn):@(server.MainAllocation.Port)
</div>
<div class="card-text fs-6">
@if (StatusCache.ContainsKey(server))
{
var status = StatusCache[server];
switch (status)
{
case "offline":
<span class="text-danger">
<TL>Offline</TL>
</span>
break;
case "stopping":
<span class="text-warning">
<TL>Stopping</TL>
</span>
break;
case "starting":
<span class="text-warning">
<TL>Starting</TL>
</span>
break;
case "running":
<span class="text-success">
<TL>Running</TL>
</span>
break;
case "failed":
<span class="text-gray-400">
<TL>Failed</TL>
</span>
break;
default:
<span class="text-danger">
<TL>Offline</TL>
</span>
break;
}
}
else
{
<span class="text-gray-400">
<TL>Loading</TL>
</span>
}
</div>
</div>
</div>
</a>
} }
</div> </div>
} </div>
</div> </div>
</div> </div>
</div> </div>
</div> }
</div> </div>
}
</div>
</LazyLoader> </LazyLoader>
@code @code

View File

@@ -1,97 +0,0 @@
{
"Moonlight": {
"AppUrl": "http://your-moonlight-url.test",
"Database": {
"Database": "database_name",
"Host": "your-moonlight-database-host.de",
"Password": "s3cr3t",
"Port": "10324",
"Username": "user_name"
},
"DiscordBotApi": {
"Enable": false,
"Token": "you api key here"
},
"DiscordBot": {
"Enable": false,
"Token": "Discord.Token.Here",
"PowerActions": false
},
"Domains": {
"_comment": "Cloudflare Api Credentials",
"AccountId": "Account Id here",
"Email": "Cloudflare Email here",
"Key": "Api Key Here"
},
"Html": {
"Headers": {
"Color": "#4b27e8",
"Description": "the next generation hosting panel",
"Keywords": "moonlight",
"Title": "Moonlight - moonlight.tld"
}
},
"Marketing": {
"BrandName": "My cool project",
"Imprint": "https://mycoolproject.de/imprint",
"Privacy": "https://mycoolproject.de/privacy",
"Website": "https://mycoolproject.de/"
},
"OAuth2": {
"_exampleProviders": [
{
"Id": "discord",
"ClientId": "",
"ClientSecret": ""
},
{
"Id": "google",
"ClientId": "",
"ClientSecret": ""
}
],
"Providers": [],
"EnableOverrideUrl": false,
"OverrideUrl": "http://your-moonlight-url.test"
},
"Security": {
"Token": "RANDOM UUID HERE"
},
"Mail": {
"Email": "no-reply@mycoolproject.de",
"Server": "mycoolproject.de",
"Password": "s3cr3t",
"Port": 465,
"Ssl": true
},
"Cleanup": {
"Cpu": 90,
"Memory": 8192,
"Wait": 15,
"Uptime": 6,
"Enable": false,
"MinUptime": 10
},
"Subscriptions": {
"_comment": "Not implemented",
"SellPass": {
"Enable": false,
"Url": ""
}
},
"DiscordNotifications": {
"Enable": false,
"WebHook": ""
},
"Statistics": {
"Enabled": true,
"Wait": 15
},
"Rating": {
"Enabled": true,
"Url": "link-to-google.page",
"MinRating": 4,
"DaysSince": 5
}
}
}